class Loa::ProgramaDeGoverno < ApplicationRecord
	include IncrementadorDeCodigoConcern

	has_paper_trail

	after_initialize :sugestao_de_codigo, if: :new_record?

	after_create :set_programa_de_governo_id

	belongs_to :orcamento, required: true
	belongs_to :tipo_de_programa
	belongs_to :programa, class_name: 'Ppa::Programa'
	belongs_to :solicitacao_de_alteracao_orcamentaria, class_name: 'Contabilidade::SolicitacaoDeAlteracaoOrcamentaria'

	has_many :acoes, dependent: :restrict_with_exception
	has_many :subacoes, through: :acoes
	has_many :orcamentos_da_despesa, through: :subacoes
	has_many :metas_fisicas, through: :subacoes

	validates_presence_of :orcamento_id, :codigo, :nome, :status_do_orcamento
	validates_presence_of :tipo_de_programa_id, if: Proc.new { self.eh_progama? }
	validates_uniqueness_of :codigo, scope: :orcamento_id
	validates_uniqueness_of :nome, scope: [:orcamento_id, :codigo]

	validates_length_of :codigo, is: 4
	validates_length_of :nome, maximum: 120

	before_update :nao_permitir_mudar_tipo_do_programa_com_porcentagem_presente

	enum status_do_orcamento: STATUS_DO_ORCAMENTO

	scope :programas_de_governo_tematicos,  -> { where('tipo_de_programa_id = ?', Loa::TipoDePrograma.find_by_codigo("1").id ) }
	scope :programas_de_governo_de_gestao,  -> { where('tipo_de_programa_id = ?', Loa::TipoDePrograma.find_by_codigo("2").id ) }

	def nao_permitir_mudar_tipo_do_programa_com_porcentagem_presente
		self.subacoes.each do |subacao|
			subacao.temas_da_subacao.each do |tema|
				if tema.percentual.present? && tema.percentual != 0 && self.tipo_de_programa_id_changed?
					errors.add(:tipo_de_programa_id, "Não é possível alterar o tipo do programa, pois existe percentual de um orçamento temático associado")
					return false
				end
			end
		end
	end

	def to_sim
		texto = ""
		texto << "205".to_s.sim_preenche(3) + ","
		texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","
		texto << orcamento.exercicio.to_s + '00' + "," #3
		texto << codigo.to_s.sim_preenche(4) + "," #Codigo do programa de governo
		texto << nome.to_s.sim_limite(120) #Nome do programa de governo
		return texto
	end

	def importado_do_ppa?
		self.programa_id.present?
	end

	def codigo_e_nome
		"#{codigo} - #{nome}"
	end

	def valor_total_fixado_da_despesa
		self.subacoes.inject(0) { |total, subacao|
			total + subacao.valor_total_fixado_da_despesa.to_f
		}
	end

	def valor_despesa_na_acao
		self.subacoes.inject(0) { |total, subacao| total + subacao.valor_total_fixado_da_despesa.to_f}
	end	

	def valor_total_fixado_da_despesa_por_unidade unidade
		if unidade.programas_de_governo.include? self
			return self.subacoes.where(unidade_orcamentaria_id: unidade.id).inject(0) { |total, subacao|
				total + subacao.valor_total_fixado_da_despesa.to_f
			}
		else
			return 0
		end
	end

	def valores_por_unidades_orcamentarias
		unidades_orcamentarias = Array.new
		unidades_orcamentarias_agrupadas = Array.new
		valor = 0
		subacoes.each { |subacao|
			unidades_orcamentarias << {
				nome: subacao.unidade_orcamentaria.try(:nome),
				valor: subacao.fixacao_da_despesa.to_f,
				tipo: subacao.try(:tipo_de_orcamento).try(:codigo)
			 }
		}
		unidades_orcamentarias = unidades_orcamentarias.sort_by { |h| h[:nome] }.reverse!
		unidades_orcamentarias.each_with_index { |unidade, index|
			valor += unidade[:valor].to_f
			unless unidades_orcamentarias[index + 1] && unidade[:nome] == unidades_orcamentarias[index + 1][:nome]
				unidades_orcamentarias_agrupadas << {
					nome: unidade[:nome],
					valor: valor.to_f,
					tipo: unidade[:tipo]
				}
				valor = 0
			end
		}
		return unidades_orcamentarias_agrupadas
	end

	def valor_total_fixado_da_despesa_orcamentos_fiscal
		self.subacoes.orcamentos_fiscal.inject(0) { |total, subacao|
			total + subacao.fixacao_da_despesa.to_f
		}
	end

	def valor_total_fixado_da_despesa_seguridade_social
		self.subacoes.seguridade_social.inject(0) { |total, subacao|
			total + subacao.fixacao_da_despesa.to_f
		}
	end

	def valor_total_realizado_da_despesa
		self.subacoes.inject(0) { |total, subacao|
			total + subacao.valor_total_realizado_da_despesa.to_f
		}
	end

	def possui_acoes_iniciativas?
		self.acoes.present?
	end

	def programa_tematico?
		tipo_de_programa.try(:codigo) == 1
	end

	def importar_dados_do_programa
		ppa_programa = Ppa::Programa.find(self.programa_id)
		if ppa_programa
			self.codigo = ppa_programa.codigo_completo
			self.nome = ppa_programa.nome
			self.objetivo = ppa_programa.justificativa
			self.tipo_de_programa_id = ppa_programa.tipo_de_programa_id
		end
	end

	def eh_progama?
		self.programa.present?
	end

	private
	def sugestao_de_codigo
		if (orcamento.present? && codigo.blank? && tipo_de_programa.present?)
			valor_minimo = self.tipo_de_programa.codigo == 1 ? 1001 : 2001
			gerar_sugestao_codigo( :codigo, 4, {orcamento_id: orcamento.id, tipo_de_programa_id: self.tipo_de_programa.id}, true, valor_minimo )
		end
	end

	def set_programa_de_governo_id
		if self.solicitacao_de_alteracao_orcamentaria_id.present?
			@solicitacao_de_alteracao_orcamentaria = Contabilidade::SolicitacaoDeAlteracaoOrcamentaria.find(solicitacao_de_alteracao_orcamentaria_id)
			@solicitacao_de_alteracao_orcamentaria.update(programa_de_governo_da_solicitacao_id: self.id)
			@solicitacao_de_alteracao_orcamentaria.save!
		end
	end

	def self.dados_relatorio und_orcamentaria, orcamento_atual
		dados_tematico = []
		dados_gestao = []

		if und_orcamentaria > 0
			@programas_de_governo_tematicos = Loa::ProgramaDeGoverno.joins(acoes: [subacoes: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: und_orcamentaria}).programas_de_governo_tematicos
			@programas_de_governo_de_gestao = Loa::ProgramaDeGoverno.joins(acoes: [subacoes: :unidade_orcamentaria]).where(loa_unidades_orcamentarias: {id: und_orcamentaria}).programas_de_governo_de_gestao
		else
			@programas_de_governo_tematicos = orcamento_atual.programas_de_governo.programas_de_governo_tematicos
			@programas_de_governo_de_gestao = orcamento_atual.programas_de_governo.programas_de_governo_de_gestao
		end

		@programas_de_governo_tematicos.each do |programa_tematico|
			valor_total_programa = 0
			array_tematico = []
			array_unidades_tematico = []

			dados_tematico << {
				"programa": programa_tematico.codigo_e_nome,
				"valor_do_programa": programa_tematico.valor_total_fixado_da_despesa.try(:contabil),
				"objetivo": programa_tematico.objetivo.to_s
			}

			programa_tematico.acoes.each do |acao|
				subacoes = und_orcamentaria > 0 ? acao.subacoes.joins(:unidade_orcamentaria).where(loa_unidades_orcamentarias: {id: und_orcamentaria}) : acao.subacoes
				valor_total_programa += acao.valor_total_fixado_da_despesa

				array_tematico <<
					{
						"nome": acao.try(:nome),
						"subacao": subacoes.map{|subacao| subacao.unidade_orcamentaria.codigo_e_nome },
						"valor": acao.valor_total_fixado_da_despesa.contabil,
					}

				array_unidades_tematico <<
					{
						"unidade_orcamentaria": subacoes.map{|subacao| subacao.unidade_orcamentaria.codigo_e_nome },
						"orgao": subacoes.map{|subacao| subacao.unidade_orcamentaria.orgao.codigo_e_nome },
					}
			end
				array_tematico << {"nome": "Total", "valor": valor_total_programa.contabil}
				dados_tematico.last.merge!(acoes_tematico: array_tematico, unidades_tematicas: array_unidades_tematico )
		end

			@programas_de_governo_de_gestao.each do |programa_gestao|
				valor_total_programa = 0
				array_gestao = []
				array_unidades_gestao= []
				dados_gestao << {
					"programa": programa_gestao.codigo_e_nome,
					"valor_do_programa": programa_gestao.valor_total_fixado_da_despesa.try(:contabil),
					"objetivo": programa_gestao.objetivo.to_s
				}

				programa_gestao.acoes.each do |acao|
					subacoes = und_orcamentaria > 0 ? acao.subacoes.joins(:unidade_orcamentaria).where(loa_unidades_orcamentarias: {id: und_orcamentaria}) : acao.subacoes
					valor_total_programa += acao.valor_total_fixado_da_despesa

					array_gestao <<
						{
							"nome": acao.try(:nome),
							"subacao": subacoes.map{|subacao| subacao.unidade_orcamentaria.codigo_e_nome },
							"valor": acao.valor_total_fixado_da_despesa.contabil
						}

					array_unidades_gestao <<
						{
							"unidade_orcamentaria": subacoes.map{|subacao| subacao.unidade_orcamentaria.codigo_e_nome },
							"orgao": subacoes.map{|subacao| subacao.unidade_orcamentaria.orgao.codigo_e_nome },
						}

				end
				array_gestao << {"nome": "Total", "valor": valor_total_programa.contabil}

				dados_gestao.last.merge!(acoes_gestao:array_gestao, unidades_gestao: array_unidades_gestao)
		end
			return dados_gestao, dados_tematico
	end

end
