class Loa::Subacao < ApplicationRecord
	include IncrementadorDeCodigoConcern

	has_paper_trail

	attr_default :fixacao_da_despesa, 0.0

	belongs_to :acao, inverse_of: :subacoes, required: true
	belongs_to :unidade_orcamentaria, required: true
	belongs_to :funcao, class_name:'Base::Funcao', required: true
	belongs_to :subfuncao, class_name: 'Base::Subfuncao', required: true
	belongs_to :tipo_de_orcamento, class_name: 'Base::TipoDeOrcamento', required: true

	has_many :metas_fisicas, dependent: :destroy
	has_many :elementos_de_despesa_por_subacao, dependent: :destroy
	has_many :orcamentos_da_despesa, through: :elementos_de_despesa_por_subacao
	has_many :elementos_de_despesa, through: :elementos_de_despesa_por_subacao
	has_many :temas_da_subacao, inverse_of: :subacao, class_name: 'Loa::TemaDaSubacao', dependent: :destroy

	accepts_nested_attributes_for :elementos_de_despesa_por_subacao, allow_destroy: true
	accepts_nested_attributes_for :temas_da_subacao, allow_destroy: true

	validates_associated :temas_da_subacao

	before_validation :preenche_tipo_de_orcamento
	before_validation :atribui_codigo_da_subacao

	validates_presence_of :acao
	validates_presence_of :unidade_orcamentaria_id
	validates_presence_of :funcao_id
	validates_presence_of :subfuncao_id
	validates_presence_of :tipo_de_orcamento_id
	validates_presence_of :codigo
	validates_presence_of :fixacao_da_despesa, unless: :de_alteracao_orcamentaria?
	validates_presence_of :status_do_orcamento

	validates_length_of :codigo, is: 4

	validates_numericality_of :fixacao_da_despesa, greater_than_or_equal_to: 0, unless: :de_alteracao_orcamentaria?

	validates :acao_id, immutable: true
	validates :codigo, immutable: true

	validates :fixacao_da_despesa, valor_inserido_equals_valor_orcado: { associacao: :elementos_de_despesa_por_subacao, orcamento: :orcamentos_da_despesa, mensagem: 'valor da fixação não corresponde ao total da despesa' }, unless: :de_alteracao_orcamentaria?
	validates :elementos_de_despesa_por_subacao, uniq_nested_attributes: { atributo: :elemento_de_despesa_id, mensagem: "elemento de despesa deve ser único dentro da subação" }

	validate :acao_deve_ter_apenas_uma_subacao, on: :create, unless: Proc.new { self.acao.present? && self.acao.programa_de_governo.orcamento.trabalha_com_subacao? }
	validate :valida_tipo_de_unidade_com_natureza_da_acao
	validate :percentual_total_dos_temas

	scope :orcamentos_fiscal, -> { joins(:tipo_de_orcamento).where('base_tipos_de_orcamento.codigo = ?', 'F') }
	scope :seguridade_social, -> { joins(:tipo_de_orcamento).where('base_tipos_de_orcamento.codigo = ?', 'S') }

	enum status_do_orcamento: STATUS_DO_ORCAMENTO
	enum despesa_da_educacao: { "Outros": 1, "FUNDEB 60": 2, "FUNDEB 40": 3 }

	before_destroy :valida_apagar_subacao, if: proc { self.acao.trabalha_com_subacao? }

	def valida_apagar_subacao
		if valor_total_fixado_da_despesa > 0
			raise ActiveRecord::DeleteRestrictionError.new "subação com valor orçado não pode ser apagada."
		end
	end

	def classificacao_funcional_programatica mostra_codigo_subacao: true
		"#{funcao.try(:codigo)}.#{subfuncao.try(:codigo)}.#{acao.try(:programa_de_governo).try(:codigo)}.#{acao.try(:codigo_completo).to_s.gsub(".","")}#{"."+codigo if mostra_codigo_subacao}"
	end

	def classificacao_funcional_programatica_nao_mostra_codigo_subacao
		"#{funcao.try(:codigo)}.#{subfuncao.try(:codigo)}.#{acao.try(:programa_de_governo).try(:codigo)}.#{acao.try(:codigo_completo).to_s.gsub(".","")}"
	end

	def classificacao_funcional_programatica_com_descricao
		"#{funcao.try(:codigo)}.#{subfuncao.try(:codigo)}.#{acao.try(:programa_de_governo).try(:codigo)}.#{acao.try(:codigo_completo).to_s.gsub(".","")} #{acao.try(:nome)}"
	end

	def acao_iniciativa
		"#{acao.try(:codigo_e_nome)}"
	end

	def classificacao_com_uo_e_ug
		"#{self.unidade_orcamentaria.codigo_completo} #{self.funcao&.codigo} #{self.subfuncao&.codigo} #{self&.acao&.programa_de_governo&.codigo} #{self&.acao&.codigo}"
	end

	def valor_total_fixado_da_despesa
		if acao.present? && acao.solicitacao_de_alteracao_orcamentaria.present? && acao.solicitacao_de_alteracao_orcamentaria.orcamento.id > orcamento.id
			try(:orcamentos_da_despesa).sum(&:valor_total_acrescentado_na_alteracao).to_d
		else
			try(:orcamentos_da_despesa).sum(:valor).to_d
		end
	end

	def valor_total_fixado_da_despesa_para_o_sim
		if acao.present? && acao.solicitacao_de_alteracao_orcamentaria.present?
			try(:orcamentos_da_despesa).sum(&:valor_total_acrescentado_na_alteracao).to_d
		else
			try(:orcamentos_da_despesa).sum(:valor).to_d
		end
	end

	def valor_total_loa_e_creditos
		try(:orcamentos_da_despesa).sum(&:valor_loa_e_creditos)
	end

	def valor_total_orcado_da_despesa
		try(:orcamentos_da_despesa).sum(:valor_orcado).to_f
	end

	def valor_total_realizado_da_despesa
		try(:orcamentos_da_despesa).sum(:valor_empenhado).to_f
	end

	def valor_total_fixado_da_despesa_por_funcao
		self.orcamentos_da_despesa.sum(:valor).to_d
	end
	
	def valor_total_a_empenhar
		valor_total_fixado_da_despesa - valor_total_realizado_da_despesa
	end

	def valor_total_fixado_por_fonte_de_recursos(fonte_de_recursos_id)
		(self.orcamentos_da_despesa.where(fonte_de_recursos_id: fonte_de_recursos_id).sum(:valor)).to_f
	end

	def orcamento
		acao.try(:programa_de_governo).try(:orcamento)
	end

	def possui_lancamentos_na_despesa?
		return orcamentos_da_despesa.present?
	end

	def tipo_orcamento_fiscal?
		return try(:tipo_de_orcamento).try(:codigo).eql?('F')
	end

	def tipo_seguridade_social?
		return try(:tipo_de_orcamento).try(:codigo).eql?('S')
	end

	def de_alteracao_orcamentaria?
		acao.try(:solicitacao_de_alteracao_orcamentaria).present? || acao.try(:programa_de_governo).try(:solicitacao_de_alteracao_orcamentaria).present?
	end

	def lista_de_temas
		temas_do_orcamento = orcamento.temas_do_orcamento

		temas_da_subacao.each do |tema|
			temas_da_subacao.destroy(tema) unless temas_do_orcamento.pluck(:orcamento_tematico_id).include? tema.orcamento_tematico.id
		end

		temas_do_orcamento.each do |tema|
			temas_da_subacao.build({ orcamento_tematico_id: tema.orcamento_tematico_id }) unless temas_da_subacao.map(&:orcamento_tematico_id).include? tema.orcamento_tematico.id
		end

		temas_da_subacao
	end

	def to_sim
		begin
			texto = ""
			texto << "203".to_s.sim_preenche(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","
			texto << unidade_orcamentaria.orgao.orcamento.exercicio.to_s.sim_limite_sem_aspas(4, "00") + ","
			texto << unidade_orcamentaria.orgao.codigo.to_s.sim_preenche(2) + ","#Código do Órgão
			texto << unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + "," #Código da Unidade Orçamentária
			texto << funcao.codigo.to_s.sim_preenche(2) + "," #Código da Função
			texto << subfuncao.codigo.to_s.sim_preenche(3) + "," #Código da Subfunção
			texto << acao.programa_de_governo.codigo.to_s.sim_preenche(4) + "," #Código do Programa
			texto << acao.natureza_da_acao.codigo.to_s.sim_limite(1) + "," #Código de Projeto ou Atividade
			texto << acao.codigo.to_s.sim_preenche(3) + "," #Número do Projeto ou Atividade
			texto << codigo.to_s.sim_preenche(4) + "," #Número do Sub-projeto ou Sub-atividade
			texto << tipo_de_orcamento.codigo.to_s.sim_limite(1) + "," #Código do Tipo de Orçamento
			texto << acao.nome.to_s.sim_limite(120) + "," #Nome do Projeto ou Atividade
			texto << acao.programa_de_governo.objetivo.to_s.gsub(/["\r\n]/, "").sim_limite(255)+ "," #Descrição do Projeto ou Atividade
			texto << valor_total_fixado_da_despesa_para_o_sim.to_s.sim_valor
			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.codigo, atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def atribui_codigo_da_subacao
		gerar_sugestao_codigo( :codigo, 4, {acao_id: acao.id} ) if (acao.present? && codigo.blank?)
	end

	def preenche_tipo_de_orcamento
		if orcamento.present? && orcamento.tipos_de_orcamento.present?
			self.tipo_de_orcamento_id = self.try(:funcao).try(:tipo_de_orcamento_id)
		end
	end

	private
	def valida_tipo_de_unidade_com_natureza_da_acao
		if unidade_orcamentaria.present?
			if unidade_orcamentaria.reserva_de_contingencia? && !acao.reserva_de_contingencia?
				if de_alteracao_orcamentaria?
					errors.add(:natureza_da_acao_id, "deve ser selecionado o tipo reserva de contingência, pois a Unidade Orçamentária é do tipo reserva de contingência")
				else
					errors.add(:unidade_orcamentaria_id, "não pode ser selecionada uma Unidade Orçamentária de reserva de contingência, pois a Ação não é do tipo reserva de contingência")
				end
			elsif !unidade_orcamentaria.reserva_de_contingencia? && acao.reserva_de_contingencia?
				if de_alteracao_orcamentaria?
					errors.add(:natureza_da_acao_id, "não pode ser selecionado o tipo reserva de contingência, pois a Unidade Orçamentária não é do tipo reserva de contingência")
				else
					errors.add(:unidade_orcamentaria_id, "deve ser selecionada uma Unidade Orçamentária de reserva de contingência, pois a Ação é do tipo reserva de contingência")
				end
			end
		end
	end

	def acao_deve_ter_apenas_uma_subacao
		errors.add(:base, "essa ação não pode ter mais de uma subação") if acao.present? && acao.subacoes.any? && acao.subacoes.count > 0
	end

	def percentual_total_dos_temas
		errors.add(:temas_da_subacao, "soma dos percentuais deve ser menor ou igual a 100%") if temas_da_subacao.inject(0){|total, tema| total + tema.percentual.to_f}.to_f > 100
	end
end
