class Contabilidade::Decreto < ApplicationRecord
	has_paper_trail
	include AASM
	include TradutorConcern
	include GeradorDeEventosContabeis
	include ActionView::Helpers

	attr_default :status, :aberto
	attr_default :tipo_de_decreto, :credito

	belongs_to :orcamento, class_name: 'Orcamento'

	has_many :solicitacao_de_alteracao_orcamentarias
	has_many :solicitacao_de_alteracao_orcamentarias_nao_confirmadas, -> { where.not(status: Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.status[:confirmado]) }, class_name: "Contabilidade::SolicitacaoDeAlteracaoOrcamentaria"
	has_many :dotacoes_origem, through: :solicitacao_de_alteracao_orcamentarias
	has_many :dotacoes_destino, through: :solicitacao_de_alteracao_orcamentarias
	has_many :contigenciamentos, class_name: 'Contabilidade::Contigenciamento'
	has_many :documentos_do_decreto, class_name: 'Contabilidade::DocumentoDoDecreto'
	# RPPS
	has_many :conselhos_de_rpps, class_name: 'Contabilidade::ConselhoDeRpps'
	has_many :parcelamentos_de_rpps_de_acordo, class_name: 'Contabilidade::ParcelamentoDoRpps', foreign_key: :decreto_de_acordo_id
	has_many :parcelamentos_de_rpps_de_lei, class_name: 'Contabilidade::ParcelamentoDoRpps', foreign_key: :decreto_de_lei_id

	has_many :orcamentos_da_despesa_origem, through: :dotacoes_origem, source: :orcamento_da_despesa
	has_many :orcamentos_da_despesa_destino, through: :dotacoes_destino, source: :orcamento_da_despesa

	validates_presence_of :tipo_de_legislacao, :numero, :data_da_legislacao
	validates_presence_of :numero_da_lei, unless: Proc.new { self.rpps? }
	validates_presence_of :classificacao, unless: Proc.new { self.rpps? }
	validates_presence_of :funcao_do_instrumento_legal,:data_da_publicacao, :ementa_do_instrumento_legal, if: Proc.new { self.rpps? }
	validates_presence_of :tipo_de_credito, unless: Proc.new { self.contigenciamento? || self.rpps? }
	validates_presence_of :data_da_lei_autorizativa, if: Proc.new {self.credito_especial? && self.lei_autorizativa?}
	validates_presence_of :lei_autorizativa, if: Proc.new {self.credito_especial?}
	validates_presence_of :numero_da_lei_sim, if: Proc.new {self.credito_especial? == false}, unless: Proc.new { self.rpps? }

	validates_uniqueness_of :numero, scope: :tipo_de_credito , message: "Já existe esse número para o tipo de credito selecionado"

	validate :valida_data_pelo_exercicio, unless: Proc.new { self.rpps? }
	validate :valida_se_ja_houve_envio_do_sim

	enum tipo_de_legislacao: {
		decreto: 0,
		oficio: 1,
		portaria: 2,
		resolucao: 3,
		instrucao_normativa: 4,
		lei: 5
	}

	enum tipo_de_credito: {
		credito_suplementar: 0,
		credito_especial: 1,
		credito_extraordinario: 2
	}

	enum tipo_de_decreto: {
		credito: 0,
		contigenciamento: 1,
		rpps: 2
	}

	enum status: {
		aberto: 0,
		fechado: 1
	}

	enum classificacao: {
		remanejamento: 0,
		transposicao: 1,
		transferencias: 2,
		comum: 3,
		orcamentario: 4
	}

	enum funcao_do_instrumento_legal:{
		criacao_do_rpps: 1,
		reestruturacao_alteracao: 2,
		extincao: 3
	}

	aasm column: :status, enum: true, whiny_transitions: false do
		state :aberto, :initial => true
		state :fechado

		event :fechar do
			transitions from: :aberto, to: :fechado do
				guard do
					retorna_orcamentos_da_despesa.any? && !solicitacao_de_alteracao_orcamentarias_nao_confirmadas.any? && fontes_fechadas?
				end
			end
		end
	end

	def valida_data_pelo_exercicio
		if self.data_da_legislacao && self.orcamento && (self.data_da_legislacao.to_date.year > self.orcamento.exercicio || self.data_da_legislacao.to_date.year < self.orcamento.exercicio)
			errors.add(:data_da_legislacao, "Data do decreto deve estar no período do exercício do orçamento")
		end
	end

	def fontes_fechadas?
		if self.verifica_fechamento_do_decreto
			total_origem = valor_reajuste_origem_do_decreto.abs.to_d
			total_acrescentado = valor_reajuste_destino_do_decreto_sem_superavit.abs.to_d
			return total_origem == total_acrescentado
		else
			return true
		end
	end

	def verifica_fechamento_do_decreto
		self.solicitacao_de_alteracao_orcamentarias.select{ |sol| (sol.anulacao_de_dotacao? && sol.fonte_de_recursos.nil?) || sol.decreto_para_reducao? }.any?
	end

	def enviado_para_o_sim?
		self.arquivo_id.present? && self.arquivo_id > 0
	end

	def numero_e_classificacao
		return "#{self.numero} (#{self.localizar(:classificacao)})"
	end

	def numero_data_e_classificacao
		return "Nº: #{self.numero} | Data: #{self.data_da_legislacao} | #{self.localizar(:classificacao)}"
	end

	def numero_data_extenso
		return "Nº: #{self.numero} | Data: #{self.data_da_legislacao_por_extenso}"
	end

	def numero_extenso_com_ponto
		numero_formatado = self.numero.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1.')
		numero_formatado[0] = numero_formatado[0].upcase
	
		return " #{numero_formatado}, Data #{self.data_da_legislacao_por_extenso}"
	end
	

	def numero_data_funcao
		return "Nº: #{self.numero} | Data: #{self.data_da_legislacao} | #{self.localizar(:funcao_do_instrumento_legal)}"
	end

	def retorna_orcamentos_da_despesa
		(orcamentos_da_despesa_destino + orcamentos_da_despesa_origem).uniq
	end

	def retorna_orcamentos_da_despesa_que_nao_sao_de_anulacoes
		(retorna_orcamentos_da_despesa - retorna_orcamentos_da_despesa_de_solicitacao_de_anulacao_de_dotacao).uniq
	end

	def retorna_orcamentos_da_despesa_de_solicitacao_de_anulacao_de_dotacao
		orcamentos_da_despesa_origem_de_anulacao = orcamentos_da_despesa_origem.joins(dotacoes_origem: :solicitacao_de_alteracao_orcamentaria)
			.where(contabilidade_solicitacao_de_alteracao_orcamentarias: {
					origem_do_recurso: Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.origens_dos_recursos[:anulacao_de_dotacao]})

		orcamentos_da_despesa_destino_de_anulacao = orcamentos_da_despesa_destino.joins(dotacoes_destino: :solicitacao_de_alteracao_orcamentaria)
			.where(contabilidade_solicitacao_de_alteracao_orcamentarias: {
					origem_do_recurso: Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.origens_dos_recursos[:anulacao_de_dotacao]})

		(orcamentos_da_despesa_origem_de_anulacao + orcamentos_da_despesa_destino_de_anulacao).uniq
	end

	def retorna_valor_de_reajuste_por_fonte fonte_id
		valor_reajuste_destino = valor_reajuste_destino_por_fonte(fonte_id)
		valor_reajuste_origem = valor_reajuste_origem_por_fonte(fonte_id)
		(valor_reajuste_destino - valor_reajuste_origem).abs
	end

	def data_da_legislacao_por_extenso
		return "#{self.data_da_legislacao.strftime("%d")} DE #{l(self.data_da_legislacao, format: :mes).upcase} DE #{self.data_da_legislacao.year}"
	end

	def data_da_lei_autorizativa_por_extenso
		if data_da_lei_autorizativa.present?
			return "#{self.data_da_lei_autorizativa.strftime("%d")} DE #{l(self.data_da_lei_autorizativa, format: :mes).upcase} DE #{self.data_da_lei_autorizativa.year}"
		else
			"SEM DATA DA LEI AUTORIZATIVA"
		end
		
	end	

	def valor_reajuste_destino_por_fonte fonte_id
		solicitacao_de_alteracao_orcamentarias.joins(dotacoes_destino: [orcamento_da_despesa: :fonte_de_recursos])
			.where(contabilidade_solicitacao_de_alteracao_orcamentarias: {
				origem_do_recurso: Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.origens_dos_recursos[:anulacao_de_dotacao]})
					.where(base_fontes_de_recursos: {id: fonte_id}).sum(&:valor_para_ajustar_no_decreto)
	end

	def valor_reajuste_destino_por_orgao(orgao_id)
		self.dotacoes_destino.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]]).where("loa_unidades_orcamentarias.orgao_id = '?'", orgao_id).map{|f| f.valor.to_d}.sum rescue 0.00
	end

	def valor_reajuste_destino_por_unidade_orcamentaria_e_origem_do_recurso(unidade_orcamentaria_id, origem_do_recurso)
		self.dotacoes_destino.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao]).joins(:solicitacao_de_alteracao_orcamentaria).where("loa_subacoes.unidade_orcamentaria_id = '?' AND contabilidade_solicitacao_de_alteracao_orcamentarias.origem_do_recurso = '?'", unidade_orcamentaria_id,  origem_do_recurso).map{|f| f.valor.to_d}.sum rescue 0.00
	end

	def valor_reajuste_destino_do_decreto
		self.solicitacao_de_alteracao_orcamentarias.map{|f| f.soma_dotacoes_destino.to_d}.sum rescue 0.00
	end

	def valor_reajuste_destino_do_decreto_sem_superavit
		self.solicitacao_de_alteracao_orcamentarias.where.not(origem_do_recurso: 3).map{|f| f.soma_dotacoes_destino.to_d}.sum rescue 0.00
	end

	def valor_reajuste_origem_por_fonte fonte_id
		solicitacao_de_alteracao_orcamentarias.joins(dotacoes_origem: [orcamento_da_despesa: :fonte_de_recursos])
			.where(contabilidade_solicitacao_de_alteracao_orcamentarias: {
				origem_do_recurso: Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.origens_dos_recursos[:anulacao_de_dotacao]})
					.where(base_fontes_de_recursos: {id: fonte_id}).sum(&:valor_para_ajustar_no_decreto)
	end

	def valor_reajuste_origem_por_orgao(orgao_id)
		self.dotacoes_origem.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]]).where("loa_unidades_orcamentarias.orgao_id = '?'", orgao_id).map{|f| f.valor.to_d}.sum rescue 0.00
	end

	def valor_reajuste_origem_por_unidade_orcamentaria_e_origem_do_recurso(unidade_orcamentaria_id, origem_do_recurso)
		self.dotacoes_origem.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao]).joins(:solicitacao_de_alteracao_orcamentaria).where("loa_subacoes.unidade_orcamentaria_id = '?' AND contabilidade_solicitacao_de_alteracao_orcamentarias.origem_do_recurso = '?'", unidade_orcamentaria_id,  origem_do_recurso).map{|f| f.valor.to_d}.sum rescue 0.00
	end

	def valor_reajuste_origem_do_decreto
		self.solicitacao_de_alteracao_orcamentarias.map{|f| f.soma_dotacoes_origem.to_d}.sum rescue 0.00
	end

	def orgaos_do_decreto
		unidades_orcamentarias_ids = Array.new
		
		if self.dotacoes_origem.any? 
			unidades_orcamentarias_ids_origem = self.dotacoes_origem.map{|f| f.orcamento_da_despesa.elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria.id}
			unidades_orcamentarias_ids += unidades_orcamentarias_ids_origem
		end
		
		if self.dotacoes_destino.any?
			unidades_orcamentarias_ids_destino = self.dotacoes_destino.map{|f| f.orcamento_da_despesa.elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria.id}
			unidades_orcamentarias_ids += unidades_orcamentarias_ids_destino
		end

		Loa::Orgao.joins(:unidades_orcamentarias).where("(loa_unidades_orcamentarias.id IN (?))", unidades_orcamentarias_ids)
	end

	def classificacao_to_sim
		if remanejamento?
			return "R"
		elsif transposicao?
			return "T"
		elsif transferencias?
			return "F"
		end
	end

	def tipo_de_credito_e_classificacao
		if self.comum?
			return "#{self.localizar(:tipo_de_credito)} #{self.localizar(:classificacao)}"
		else
			return "#{self.localizar(:tipo_de_credito)} de #{self.localizar(:classificacao)}"
		end
	end

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

	def existe_lote_do_sim?
		ultima_data = data_da_legislacao

		if ultima_data.present?
			lote_sim = Tcm::Lote.find_by(
				orcamento_id: 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 enviado_para_o_sim?
		self.arquivo_id.present?
	end

	def to_sim
		# Variáveis necessárias para gerar o SIM
		begin
			exercicio_do_orcamento = orcamento.exercicio.to_s
			unidade_orcamentaria = orcamento.unidades_orcamentarias.joins(:tipo_de_unidade_administrativa).find_by('base_tipos_de_unidades_administrativas.codigo = ?', "07")
	
			texto = ""
			texto << "970".sim_preenche(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.sim_preenche(3) + ","
			texto << exercicio_do_orcamento.sim_limite_sem_aspas(4, "00") + ","
			texto << (unidade_orcamentaria.present? ? unidade_orcamentaria.orgao.codigo.sim_preenche(2) : "''") + ","
			texto << (unidade_orcamentaria.present? ? unidade_orcamentaria.codigo.codigo_uo_to_sim : "''") + ","
			texto << numero.to_s.sim_limite(10) + ","
			texto << data_da_legislacao.sim_data + ","
			texto << self.read_attribute_before_type_cast(:funcao_do_instrumento_legal).to_s.sim_limite(1) + ","
			texto << ementa_do_instrumento_legal.to_s.first(200).sim_limite(200) + ","
			texto << self.tipo_de_legislacao.to_s.upcase.sim_limite(1) + ","	
			texto << (numero_revogado.present? ? numero_revogado.to_s.sim_limite(10) : '""') + ","
			texto << (data_da_revogacao.present? ? data_da_revogacao.to_s : "0")
			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.numero, atributo_falho, coluna)
			else
				raise e
			end
		end
	end

end
