	class Contabilidade::AnulacaoDoTalaoDeReceita < ApplicationRecord
	has_paper_trail

	attr_accessor :tipo_da_index

	# include ReversorDeEventosContabeis
	include TradutorConcern
	include GeradorDeEventosContabeis
	include IncrementadorDeCodigoConcern

	# reversor_de_eventos_contabeis acao: 8, atributo_pai: 'talao_de_receita', atributo_data: 'data_da_anulacao', atributo_codigo_movimentacao: 'numero_do_talao'

	attr_accessor :unidade_orcamentaria_talao

	belongs_to :talao_de_receita, required: true
	belongs_to :orcamento
	belongs_to :arquivo, class_name: 'Tcm::Arquivo', required: false

	delegate :unidade_orcamentaria, :receita_stn, :natureza_da_receita, :conta_extra_orcamentaria, :pessoa, to: :talao_de_receita

	has_many :lancamentos_do_orcamento_da_receita, as: :modulo, class_name: "Contabilidade::LancamentoDoOrcamentoDaReceita"
	has_many :lancamentos_extraorcamentario_receita, as: :modulo
	has_many :complementos_por_fonte_da_anulacao_do_talao, class_name: "Contabilidade::ComplementoPorFonteDaAnulacaoDoTalao", dependent: :destroy

	accepts_nested_attributes_for :complementos_por_fonte_da_anulacao_do_talao, reject_if: :all_blank, allow_destroy: true

	validates_presence_of :data_da_anulacao, :historico, :valor, :tipo_de_anulacao
	validates_numericality_of :valor, other_than: 0
	validates_uniqueness_of :numero_da_anulacao, scope: :orcamento_id, case_sensitive: false, :allow_blank => true, :allow_nil => true

	validate :data_da_anulacao_maior_que_hoje, :valor_maior_que_o_saldo
	validate :data_da_anulacao_maior_que_data_do_talao
	validate :valida_se_data_esta_no_orcamento
	validate :valor_deve_ser_igual_ao_saldo, if: Proc.new { self.total? }
	validate :valor_deve_ser_diferente_ao_saldo, if: Proc.new { self.parcial? }
	validate :soma_dos_valores_por_fonte_nao_pode_passar_da_anulacao
	validate :valor_da_fonte_nao_pode_passar_da_fonte_do_talao

	validates :data_da_anulacao, date: true

	validate :verifica_saldo_conta_extra, if: proc {self.talao_de_receita.conta_extra_orcamentaria.present?}
	validate :verifica_saldo_conta_bancaria, if: proc { self.talao_de_receita.conta_bancaria_por_unidade_orcamentaria.present? }

	before_validation :verificar_se_he_receita_de_deducao

	after_save :atualiza_saldo_do_talao
	after_save :lancar_na_conta_bancaria
	after_save :lancar_movimento_orcamentario, if: Proc.new { talao_de_receita.orcamentario? }
	after_save :lancar_movimento_conta_extraorcamentaria, if: Proc.new { talao_de_receita.extra_orcamentario? }

	before_save :seta_orcamento_da_anulacao
	before_save :atribui_codigo_disponivel

	after_destroy :apagar_lancamento_na_conta
	after_destroy :atualiza_saldo_do_talao
	after_destroy :apagar_movimento_orcamentario
	after_destroy :apagar_movimento_orcamentario_extra

	scope :orcamentario, -> { joins(:talao_de_receita).where(contabilidade_taloes_de_receita: {origem_do_talao: 0}) }
	scope :extra_orcamentario, -> { joins(:talao_de_receita).where(contabilidade_taloes_de_receita: {origem_do_talao: 1}) }

	enum tipo_de_anulacao: {
		parcial: 0,
		total: 1
	}

	enum tipo_de_movimento:{
		original: 0,
		estorno: 1
	}

	def enviado_ao_sim?
		self.mes_bloqueado? || (arquivo_id.present? && arquivo_id > 0 && arquivo.lote.lote_processado_ou_enviado?) || (complementos_por_fonte_da_anulacao_do_talao.present? && complementos_por_fonte_da_anulacao_do_talao.joins(:arquivo).where('arquivo_id is not null').select {|i| i.arquivo.lote.lote_processado_ou_enviado? }.compact.any?)
	end

	def self.tipos_de_anulacao_mapeados
		tipos_de_anulacao.collect { |tipo| [tipo[0].humanize, tipo[0]] }
	end

	def self.tipos_de_movimento_mapeados
		tipos_de_movimento.collect { |tipo| [tipo[0].humanize, tipo[0]] }
	end

	def numero_do_talao
		talao_de_receita.numero_do_talao
	end

	def data_da_anulacao_maior_que_hoje
		errors.add(:data_da_anulacao, "não pode ser maior que hoje") if data_da_anulacao.present? && data_da_anulacao > Date.today
  end

	def data_da_anulacao_maior_que_data_do_talao
		errors.add(:data_da_anulacao, "não pode ser maior que a data do talão") if data_da_anulacao.present? && data_da_anulacao < self.talao_de_receita.data_do_talao
	end

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

	def valor_maior_que_o_saldo
		if valor.to_f > 0
			errors.add(:valor, "não pode ser maior que o saldo do talão") if talao_de_receita && valor.to_f > talao_de_receita.saldo.to_f
		end
	end

	def valor_deve_ser_igual_ao_saldo
		if valor.to_f > 0
			errors.add(:valor, "Deve ser igual ao saldo do talão") if talao_de_receita && (valor.to_f != talao_de_receita.saldo.to_f)
		end
	end

	def valor_deve_ser_diferente_ao_saldo
		if valor.to_f > 0
			errors.add(:valor, "Deve ser diferente do saldo do talão") if talao_de_receita && valor.to_f == talao_de_receita.saldo.to_f
		end
	end

	def soma_dos_valores_por_fonte_nao_pode_passar_da_anulacao
		soma_dos_valores_das_fonte = complementos_por_fonte_da_anulacao_do_talao.inject(0){|total, complemento_por_fonte| total + complemento_por_fonte.valor.to_f }
		soma_dos_valores_das_fonte = soma_dos_valores_das_fonte * -1 if self.talao_de_receita.try(:natureza_da_receita).try(:deducao?)
		errors.add(:valor, "A soma dos valores por fonte não pode ser maior que o valor do talão ") if self.valor.to_f < soma_dos_valores_das_fonte.round(2) && complementos_por_fonte_da_anulacao_do_talao.size > 0
	end

	def verificar_se_he_receita_de_deducao
		if self.valor > 0
			self.valor = self.valor * -1 if self.talao_de_receita.try(:natureza_da_receita).try(:deducao?)
		end
	end

	def atualiza_saldo_do_talao
		valor_anulado = talao_de_receita.anulacoes_dos_taloes_de_receita.sum(:valor)
		talao_de_receita.update_column(:valor_anulado, valor_anulado)
	end

	def verifica_saldo_conta_extra
		if self.talao_de_receita.conta_extra_orcamentaria.try(:passivo?)
			if self.talao_de_receita.unidade_orcamentaria_id.present? && self.data_da_anulacao.present?

				saldo_atual = self.talao_de_receita.conta_extra_orcamentaria.saldo_consolidade_por_data_e_unidade(self.talao_de_receita.unidade_orcamentaria_id, Date.today) - (self.valor.to_d - self.valor_was.to_d)
				saldo_do_dia_do_lancamento = self.talao_de_receita.conta_extra_orcamentaria.saldo_consolidade_por_data_e_unidade(self.talao_de_receita.unidade_orcamentaria_id, self.data_da_anulacao) - (self.valor.to_d - self.valor_was.to_d)

				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 verifica_saldo_conta_bancaria
		saldo_atual = self.talao_de_receita.conta_bancaria_por_unidade_orcamentaria.saldo_atual - (self.valor.to_d - self.valor_was.to_d)
		saldo_do_dia_do_lancamento = self.talao_de_receita.conta_bancaria_por_unidade_orcamentaria.saldo_por_data(self.data_da_anulacao) - (self.valor.to_d - self.valor_was.to_d)

		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

	def atualiza_tipo_de_anulacao
		if valor.to_f == talao_de_receita.valor.to_f
			update_column(:tipo_de_anulacao, :total)
		else
			update_column(:tipo_de_anulacao, :parcial)
		end
	end

	def dados_da_movimentacao_bancaria
		{
			conta_bancaria_por_unidade_orcamentaria_id: talao_de_receita.conta_bancaria_por_unidade_orcamentaria.id,
			modulo: self,
			data_da_movimentacao: data_da_anulacao,
			historico: "Referente à anulação #{tipo_de_anulacao} do Talão #{talao_de_receita.numero_do_talao}."
		}
	end

	def lancar_na_conta_bancaria
		return false if talao_de_receita.conta_bancaria_por_unidade_orcamentaria.blank? || valor.blank?

		movimentacao = Contabilidade::MovimentacaoDaContaBancaria.find_or_create_by!(dados_da_movimentacao_bancaria)

		if movimentacao.valor.present? && movimentacao.valor != self.valor
			movimentacao.try(:destroy)
			movimentacao = Contabilidade::MovimentacaoDaContaBancaria.create!(dados_da_movimentacao_bancaria)
		end

		movimentacao.update_attribute(:valor, valor * -1)
	end

	def apagar_lancamento_na_conta
		return false if talao_de_receita.conta_bancaria_por_unidade_orcamentaria.blank?
	
		movimentacoes_da_conta = Contabilidade::MovimentacaoDaContaBancaria.where(modulo: self).all
		movimentacoes_da_conta.destroy_all
	end

	def lancar_movimento_orcamentario
		ActiveRecord::Base.transaction do
			if self.valor.to_f != 0
				if lancamentos_do_orcamento_da_receita.any?
					lancamentos_do_orcamento_da_receita.each do |lancamento|
						complemento_por_fonte = complementos_por_fonte_da_anulacao_do_talao.find_by(orcamento_da_receita_id: lancamento.orcamento_da_receita.id)
						apagar_movimento_orcamentario if complemento_por_fonte.present? && lancamento.try(:valor) != complemento_por_fonte.valor
					end
				end
				cria_lancamentos_do_orcamento
			end
		end
	end

	def valor_da_fonte_nao_pode_passar_da_fonte_do_talao
		if talao_de_receita.present?
			complementos_da_fonte_do_talao = talao_de_receita.complementos_por_fonte_do_talao_de_receita
			complementos_por_fonte_da_anulacao_do_talao.each do |complemento_da_fonte|
				if complemento_da_fonte.valor.present?
					complemento_equivalente = complementos_da_fonte_do_talao.find_by(orcamento_da_receita_id: complemento_da_fonte.orcamento_da_receita_id)
					complemento_equivalente.valor = complemento_equivalente.valor * -1 if self.talao_de_receita.try(:natureza_da_receita).try(:deducao?)
					errors.add(:erro_fonte, "O valor da anulação da fonte não pode ser maior do que o lançado no talão") if  complemento_da_fonte.valor.to_f > complemento_equivalente.valor.to_f
				end
			end
		end
	end

	def lancar_movimento_conta_extraorcamentaria
		ActiveRecord::Base.transaction do
			if self.valor.to_f != 0
				if self.lancamentos_extraorcamentario_receita.any?
					self.lancamentos_extraorcamentario_receita.each do |lancamento|
						apagar_movimento_orcamentario_extra if lancamento.try(:valor) != self.valor.to_f
					end
				end
				cria_lancamentos_da_conta_extraorcamentaria
			end
		end
	end

	def cria_lancamentos_do_orcamento
		if talao_de_receita.present? && talao_de_receita.orcamentos_da_receita.any? && self.valor.to_f != 0
			self.complementos_por_fonte_da_anulacao_do_talao.each do |complemento_da_fonte|
				movimentacao = Contabilidade::LancamentoDoOrcamentoDaReceita.new(
					orcamento_da_receita: complemento_da_fonte.orcamento_da_receita,
					data_do_lancamento: self.data_da_anulacao,
					valor: complemento_da_fonte.valor * -1,
					modulo: self
				)
				movimentacao.save
			end
		end
	end

	def cria_lancamentos_da_conta_extraorcamentaria
		if self.talao_de_receita.present? && valor != 0
			valor_receita =  self.valor.to_f

			movimentacao = Contabilidade::LancamentoExtraorcamentarioReceita.new(
				conta_extra_orcamentaria_id: self.talao_de_receita.conta_extra_orcamentaria_id,
				data_do_lancamento: self.data_da_anulacao,
				valor: valor_receita * -1,
				modulo: self
			)
			movimentacao.save
		end
	end

	def apagar_movimento_orcamentario
		lancamentos_do_orcamento_da_receita.try(:destroy_all)
	end

	def apagar_movimento_orcamentario_extra
		lancamentos_extraorcamentario_receita.try(:destroy_all)
	end

	def atribui_codigo_disponivel
		gerar_codigo(data_da_anulacao, :numero_da_anulacao, :data_da_anulacao, :orcamento_id, self.orcamento_id)
	end

	def seta_orcamento_da_anulacao
		self.orcamento_id = self.talao_de_receita.orcamento_id
	end
end
