class Obra::Transferencia < ApplicationRecord
	has_paper_trail

	include TradutorConcern
	include IncrementadorDeCodigoConcern

	attr_accessor :orgao_id

	belongs_to :obra, class_name: 'Contabilidade::Obra'
	belongs_to :unidade_orcamentaria, class_name: 'Loa::UnidadeOrcamentaria', required: true
	belongs_to :orgao_cedente, class_name: 'Base::Pessoa', foreign_key: "orgao_cedente_id", required: true
	belongs_to :funcao, class_name: 'Base::Funcao'
	belongs_to :natureza_da_receita, class_name: 'Base::NaturezaDaReceita'
	belongs_to :natureza_da_receita_aplicacao_fin, class_name: 'Base::NaturezaDaReceita', foreign_key: "natureza_da_receita_aplicacao_fin_id"
	belongs_to :conta_bancaria, class_name: "Base::ContaBancaria"
	belongs_to :pre_convenio, class_name: "Obra::PreConvenio"
	belongs_to :unidade_orcamentaria_arrecadadora, class_name: 'Loa::UnidadeOrcamentaria', required: true

	has_many :aditivos_do_convenio, class_name: "Obra::AditivoDoConvenio", dependent: :destroy
	has_many :taloes_da_receita, class_name: "Contabilidade::TalaoDeReceita"

	validates_presence_of :data, :tipo, :modalidade, :conta_bancaria, :esfera
	validates_presence_of :inicio_da_vigencia, :fim_da_vigencia, :valor_contrapartida, :valor_repasse, :informacao_complementar,if: Proc.new { self.convenio? }

	validate :valida_valor_total
	validate :data_fim_da_vigencia_nao_pode_ser_anterior_a_data_de_inicio
	validate :data_de_inicio_nao_pode_ser_antes_ou_depois_da_obra
	validate :valida_data_com_exercicio, if: Proc.new { self.data_changed? }

	before_create :gera_codigo_transferencia
	before_update :gera_codigo_transferencia, if: Proc.new { self.tipo_changed?}

	before_save :marca_status_como_nil, if: :transferencia?
	before_save :altera_status, if: :convenio?

	enum tipo: { transferencia: 0, convenio: 1 }
	enum esfera: { federal: 1, estadual: 2, municipal: 3 }

	enum status: {
		pendente_finalizar_cadastro: 1,
		aguardando_inicio_da_execucao: 2,
		em_execucao: 3,
		em_execucao_aditado: 4,
		executado: 5,
		aguardando_prestacao_de_contas: 6,
		prestado_contas: 7,
		expirado: 8
	}

	enum modalidade: {
		sus: 1,
		fnde: 2,
		fnas: 3,
		convenio_sus: 4,
		convenio_exceto_sus: 5,
		contrato_de_repasse: 6,
		outros: 7
	}

	enum informacao_complementar: {
		transferencia_individual: 3110,
		transferencia_bancada: 3120,
		sem_complemento: 0000
	}
	

	# BOOLEANS
	def pode_prestar_contas?
		!prestado_contas? && (executado? || aguardando_prestacao_de_contas?)
	end

	# VALIDAÇÕES
	def data_fim_da_vigencia_nao_pode_ser_anterior_a_data_de_inicio
		unless inicio_da_vigencia.nil? || fim_da_vigencia.nil?
			errors.add(:fim_da_vigencia, "deve ser maior que a data de início da vigência") if fim_da_vigencia < inicio_da_vigencia
		end
	end

	def data_de_inicio_nao_pode_ser_antes_ou_depois_da_obra
		unless inicio_da_vigencia.nil? || obra.nil?
			errors.add(:inicio_da_vigencia, "deve ser maior ou igual a data de início da obra: #{obra.data_de_inicio}") if inicio_da_vigencia < obra.data_de_inicio
			errors.add(:inicio_da_vigencia, "deve ser menor ou igual a data de previsão de término da obra: #{obra.data_de_inicio}") if inicio_da_vigencia > obra.data_prevista_de_termino
		end
	end

	def valida_data_com_exercicio
		if self.data.present? && self.unidade_orcamentaria.present? && self.data.try(:year) != self.unidade_orcamentaria.orgao.orcamento.exercicio
			errors.add(:data, 'A data, não está dentro do exercício logado.')
		end
	end

	def valida_natureza_da_receita
		if !natureza_da_receita.present?
			raise "Não existe natureza da receita cadastrada para esta Transferência: " << "#{self.codigo_transferencia}" << " (id: " << "#{self.id.to_s})"
		end
	end

	# VALORES
	def altera_status
		if !cadastro_completo?
			self.status = "pendente_finalizar_cadastro"
		elsif !esta_no_periodo_de_vigencia?
			self.status = "aguardando_inicio_da_execucao"
		elsif esta_no_periodo_de_vigencia? && !aditivos_do_convenio.any? && !valor_da_conta_bancaria_zerou?
			self.status = "em_execucao"
		elsif esta_no_periodo_de_vigencia? && aditivos_do_convenio.any? && !valor_da_conta_bancaria_zerou?
			self.status = "em_execucao_aditado"
		elsif passou_do_periodo_de_vigencia? && !valor_da_conta_bancaria_zerou?
			self.status = "expirado"
		elsif valor_da_conta_bancaria_zerou?
			self.status = "executado"
		elsif self.executado? && data_atual_maior_que_data_do_ultimo_pagamento_da_conta_bancaria_do_convenio_mais_30_dias?
			self.status = "aguardando_prestacao_de_contas"
		end
	end

	def prestar_conta
		update_column(:status, :prestado_contas)
	end

	def data_final_de_acordo_com_aditivos
		aditivos_do_convenio.por_prazo.maximum(:fim_da_vigencia) || self.fim_da_vigencia
	end

	def valor_dos_aditivos
		aditivos_do_convenio.sum(:valor).to_f
	end

	def valor_total_do_convenio
		self.valor_total + valor_dos_aditivos
	end

	def tempo_restante
		(self.fim_da_vigencia - Date.today).to_whole
	end

	def porcentagem_do_saldo_vigencia
		diferenca = (Date.today - self.inicio_da_vigencia).to_i.to_whole
		porcentagem = (( diferenca * 100).to_f / (self.fim_da_vigencia - self.inicio_da_vigencia).to_whole).round(2)
		porcentagem.cap_at 100
	end

	def percentual_de_execucao_pago
		if obra.present?
			valor_total_pago = obra.contas_bancarias_por_pagamento.joins(:pagamento).where(base_contas_bancarias_por_pagamento: {conta_bancaria_id: self.conta_bancaria_id}).sum("contabilidade_pagamentos.valor")
			porcentagem = self.valor_total == 0 ? 0 : ((valor_total_pago.to_f * 100).to_f / self.valor_total.to_f).round(2)
			porcentagem.cap_at 100
		else
			0
		end
	end

	def porcentagem_de_execucao_menor_ou_igual_a_50_por_cento?
		percentual_de_execucao_pago <= 50 ? true : false
	end

	def porcentagem_de_vigencia_maior_ou_igual_a_80_por_cento?
		self.porcentagem_do_saldo_vigencia >= 80 ? true : false
	end

	def to_sim(data_referencia)
		begin
			orcamento = unidade_orcamentaria.orgao.orcamento
			orgao = unidade_orcamentaria.orgao
	
			# valida_natureza_da_receita
	
			texto = ""
			texto << "807".to_s.sim_preenche(3) + "," #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + "," #2
			texto << codigo_transferencia.to_s.sim_preenche(15) + "," #3
			texto << data_referencia.to_s + "," #4
			texto << (natureza_da_receita.codigo.to_s.last(-2) + '0').sim_preenche_a_direita(15) + "," # 5
			if funcao.present?
				texto << funcao.codigo.to_s.sim_limite(2) + "," #6
			else
				texto << '"" ,' #6
			end
			texto << self.read_attribute_before_type_cast(:tipo).to_s + "," #7
			texto << orgao_cedente.nome.sim_limite(60) + "," #8
			texto << objeto.to_s.delete('"').sim_limite(255) + "," #9
			texto << conta_bancaria.agencia.banco.numero_do_banco.to_s.sim_preenche(4) + "," #10
			texto << conta_bancaria.agencia.numero_da_agencia.to_s.delete(".\/-").first(4).sim_preenche(6) + "," #11
			texto << conta_bancaria.numero_da_conta.to_s.delete(".\/-").sim_preenche(10) + "," #12
			texto << (data.present? ? data.sim_data : "0") + "," #13
			texto << (convenio? ? inicio_da_vigencia.sim_data : "0") + "," #14
			texto << (convenio? ? fim_da_vigencia.sim_data : "0") + "," #15
			texto << (convenio? ? valor_total.to_f.to_s.sim_valor : "0") + "," #16
			texto << (convenio? ? valor_contrapartida.to_f.to_s.sim_valor : "0") #17
			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

	private

	def valida_valor_total
		if self.valor_total && self.valor_repasse && self.valor_contrapartida
			errors.add(:valor_total, "valor total deve ser igual a soma dos valores de repasse e contrapartida") unless self.valor_total == (self.valor_repasse + self.valor_contrapartida)
		end
	end

	def gera_codigo_transferencia
		cod_tipo = self.transferencia? ? "T" : "C"

		if self.codigo_transferencia.present? && self.tipo_changed?
			codigo_transferencia_atualizado =  self.codigo_transferencia.delete_suffix('C').delete_suffix('T') << cod_tipo
			self.codigo_transferencia = codigo_transferencia_atualizado
		else
			sequencial = (Obra::Transferencia.count == 0) ? Obra::Transferencia.count + 1 : Obra::Transferencia.last.codigo_transferencia[8..10].to_i + 1
			self.codigo_transferencia = self.data.year.to_s << unidade_orcamentaria.codigo_completo << sequencial.digitos(3) << cod_tipo
		end
	end

	def marca_status_como_nil
		self.status = nil
	end

	def valor_total_dos_pagamentos_da_conta_bancaria_do_convenio
		total = conta_bancaria.contas_bancarias_por_pagamento.inject(0) { |total, conta_bancaria_por_pagamento|
			total + conta_bancaria_por_pagamento.valor_pago.to_f
		}
	end

	def cadastro_completo?
		if self.modulo_contabil?
			natureza_da_receita.present? && natureza_da_receita_aplicacao_fin.present?
		else
			obra.present? && funcao.present? && natureza_da_receita.present? &&
				natureza_da_receita_aplicacao_fin.present? && self.objeto.present? &&
					self.codigo_original_transferencia && self.prazo_para_prestacao_de_contas
		end
	end

	def esta_no_periodo_de_vigencia?
		Date.today.between?(self.inicio_da_vigencia, data_final_de_acordo_com_aditivos)
	end

	def passou_do_periodo_de_vigencia?
		Date.today > data_final_de_acordo_com_aditivos
	end

	def data_atual_maior_que_data_do_ultimo_pagamento_da_conta_bancaria_do_convenio_mais_30_dias?
		Date.today > conta_bancaria.pagamentos.order(data_da_solicitacao: :desc).first.data_da_solicitacao + 30
	end

	def valor_da_conta_bancaria_zerou?
		valor_total_do_convenio <= valor_total_dos_pagamentos_da_conta_bancaria_do_convenio
	end
end
