require 'active_support/concern'

module CalculaParaRelatoriosAsDespesasDoPrograma
	extend ActiveSupport::Concern

	def total_por_exercicios
		ActiveRecord::Base.connection.execute( sql_resumo_geral_por_programa ).to_a
	end

	def resumo_das_despesas( consolida = false )
		resultado = []
		dados = ActiveRecord::Base.connection.execute( sql_resumo ).to_a
		if dados.present?
			dados = dados.each.map {|dado| {valor: dado['valor']} }.each_slice(4).to_a
			dados.each_with_index do |dado_por_tipo_de_despesa, index_1|
				resultado[index_1] = {}
				dado_por_tipo_de_despesa.each_with_index do |dado, index_2|
					resultado[index_1][('valor'+index_2.to_s).to_sym] = dado[:valor].to_f
				end
			end

			resultado[2] = {}

			4.times do |index|
				resultado[2][('valor'+index.to_s).to_sym] = (resultado[0].values[index].to_f + resultado[1].values[index].to_f)
			end

			if consolida
				resultado = consolidar_tres_ultimos_exercicios(resultado)
			end

			resultado[0] = {nome: 'Despesa Corrente'}.merge(resultado[0])
			resultado[1] = {nome: 'Despesa Capital'}.merge(resultado[1])
			resultado[2] = {nome: 'Total'}.merge(resultado[2])
			resultado[2] = {estilo: {negrito: true}}.merge(resultado[2])

			unless consolida
				resultado.each do |dado|
					dado[:valor0] = dado[:valor0].real.to_s
					dado[:valor1] = dado[:valor1].real.to_s
					dado[:valor2] = dado[:valor2].real.to_s
					dado[:valor3] = dado[:valor3].real.to_s
				end
			else
				resultado.each do |dado|
					dado[:valor0] = dado[:valor0].real.to_s
					dado[:valor1] = dado[:valor1].real.to_s
				end
			end
		end

		return resultado
	end

	def consolidar_tres_ultimos_exercicios resultado
		resultado.map do |dado|
			{
				valor0: dado[:valor0],
				valor1: dado[:valor1] + dado[:valor2] + dado[:valor3]
			}
		end
	end

	def projecoes_de_despesas_por_orgaos_e_uo( consolida = false )
		dados = ActiveRecord::Base.connection.execute( sql_projecoes_de_despesas_por_orgaos_e_uo ).to_a

		if dados.present?
			dados = dados.each_slice(4).to_a
		end

		resultado = dados.map do |dado|
			dado.each_with_index.inject({nome: dado.first['nome'] }) do |hash, (projecao, index)|
				hash.merge!({"valor_#{index}".to_sym => projecao['valor']})
				if projecao['codigo'].include? '.'
					hash.merge!({estilo: {tabulacao: {posicoes:[0], quantidade: 3}}})
				else
					hash.merge!({estilo: {negrito: true}})
				end
			end
		end

		data = []
		if consolida
			resultado.each_with_index do |dado, index|
				data[index] = {}
				data[index][:estilo] = dado[:estilo]
				data[index][:nome] = dado[:nome]
				data[index][:valor_0] = dado[:valor_0].real.to_s
				data[index][:valor_1] = (dado[:valor_1].to_f + dado[:valor_2].to_f + dado[:valor_3].to_f).real.to_s
			end
		else
			resultado.each_with_index do |dado, index|
				data[index] = {}
				data[index][:estilo] = dado[:estilo]
				data[index][:nome] = dado[:nome]
				data[index][:valor_0] = dado[:valor_0].real.to_s
				data[index][:valor_1] = dado[:valor_1].real.to_s
				data[index][:valor_2] = dado[:valor_2].real.to_s
				data[index][:valor_3] = dado[:valor_3].real.to_s
			end
		end

		return data
	end

	def relatorio_de_indicadores_com_fonte
		ActiveRecord::Base.connection.execute( sql_indicadores_com_fonte ).to_a
	end

	def relatorio_de_indicadores_sem_fonte
		ActiveRecord::Base.connection.execute( sql_indicadores_sem_fonte ).to_a
	end

	def total_despesa
		ActiveRecord::Base.connection.execute( sql_total_despesa ).to_a.first['valor'].real.to_s
	end

	def metas_fisicas(meta_id,consolida)
		data = {}
		quantitativos = ActiveRecord::Base.connection.execute( sql_quantitativos(meta_id) ).to_a
		unidade = Ppa::Quantitativo.find(quantitativos[0]["id"]).try(:unidade_de_medida)
		unidade_de_medida = unidade.try(:descricao)
		utiliza_casa_decimal = unidade.try(:utiliza_casa_decimal)
		transformacao = utiliza_casa_decimal ? "contabil" : "to_i"
		if consolida
			data["0"] = quantitativos[0]["indice_de_referencia"].send(transformacao).to_s + " #{unidade_de_medida}"
			data["1"] = quantitativos[1..-1].sum {|q| q["indice_de_referencia"].to_f}.send(transformacao).to_s + " #{unidade_de_medida}"
		else
			data = quantitativos.each_with_index.inject({}) { |hash, (elemento, index)|
				hash.merge(index.to_s => elemento["indice_de_referencia"].send(transformacao).to_s + " #{unidade_de_medida}")
			}
		end
		return [data]
	end

	def sql_quantitativos(meta_id)
		%Q(
		 SELECT quantitativo.id, quantitativo.indice_de_referencia
		 FROM ppa_quantitativos quantitativo
		 WHERE quantitativo.meta_id = #{meta_id}
		 ORDER BY quantitativo.exercicio
		)
	end

	private
	def sql_resumo
		%Q(
		SELECT projecao.exercicio,
		       tipo_de_despesa.descricao AS tipo_de_despesa,
		       SUM( projecao.valor ) AS valor
		  FROM ppa_projecoes_de_despesa AS projecao
		  #{join_programa}
		  JOIN ppa_unidades_orcamentarias AS unidade_orcamentaria ON unidade_orcamentaria.id = projecao.unidade_orcamentaria_id
		  JOIN ppa_orgaos AS orgao ON orgao.id = unidade_orcamentaria .orgao_id
		  JOIN base_tipos_de_despesa AS tipo_de_despesa ON tipo_de_despesa.id = projecao.tipo_de_despesa_id
		 WHERE programa.id = #{id}
		 GROUP BY projecao.exercicio, tipo_de_despesa
		 ORDER BY 2,1
		)
	end

	def sql_resumo_geral_por_programa
		%Q(
		SELECT projecao.exercicio,
		       SUM( projecao.valor ) AS valor
		  FROM ppa_projecoes_de_despesa AS projecao
		  #{join_programa}
		  JOIN ppa_unidades_orcamentarias AS unidade_orcamentaria ON unidade_orcamentaria.id = projecao.unidade_orcamentaria_id
		  JOIN ppa_orgaos AS orgao ON orgao.id = unidade_orcamentaria .orgao_id
		 WHERE programa.id = #{id}
		 GROUP BY projecao.exercicio
		 ORDER BY 1
		)
	end

	def sql_total_despesa
		%Q(
		SELECT SUM( projecao.valor ) AS valor
		  FROM ppa_projecoes_de_despesa AS projecao
		  #{join_programa}
		  JOIN ppa_unidades_orcamentarias AS unidade_orcamentaria ON unidade_orcamentaria.id = projecao.unidade_orcamentaria_id
		  JOIN ppa_orgaos AS orgao ON orgao.id = unidade_orcamentaria .orgao_id
		  JOIN base_tipos_de_despesa AS tipo_de_despesa ON tipo_de_despesa.id = projecao.tipo_de_despesa_id
		 WHERE programa.id = #{id}
		)
	end

	def sql_indicadores_com_fonte
		%Q(
		SELECT  indicador.nome,
				unidade_de_medida.descricao,
				indicador.indice_de_referencia,
		    	indicador.ano_de_referencia,
				indicador.fonte
		  FROM ppa_indicadores indicador
		  JOIN unidades_de_medida unidade_de_medida ON (unidade_de_medida.id = indicador.unidade_de_medida_id)
		 WHERE indicador.programa_id = #{id}
		)
	end

	def sql_indicadores_sem_fonte
		%Q(
		SELECT  indicador.nome,
				unidade_de_medida.descricao,
				indicador.indice_de_referencia,
		    	indicador.ano_de_referencia
		  FROM ppa_indicadores indicador
		  JOIN unidades_de_medida unidade_de_medida ON (unidade_de_medida.id = indicador.unidade_de_medida_id)
		 WHERE indicador.programa_id = #{id}
		)
	end

	def sql_projecoes_de_despesas_por_orgaos_e_uo
		%Q(
			WITH cte_dados AS (
		  SELECT orgao.codigo::text as orgao_codigo,
		         orgao.codigo || ' - ' || orgao.sigla AS orgao_nome,
		         orgao.codigo || '.' || unidade_orcamentaria.codigo AS unidade_orcamentaria_codigo,
		         orgao.codigo || '.' || unidade_orcamentaria.codigo || ' - ' || unidade_orcamentaria.sigla AS unidade_orcamentaria_nome,
		         projecao.exercicio,
		         SUM( projecao.valor ) AS valor
		    FROM ppa_projecoes_de_despesa AS projecao
		    #{join_programa}
		    JOIN ppa_unidades_orcamentarias AS unidade_orcamentaria ON unidade_orcamentaria.id = projecao.unidade_orcamentaria_id
		    JOIN ppa_orgaos AS orgao ON orgao.id = unidade_orcamentaria.orgao_id
		   WHERE programa.id = #{id}
		     AND #{where_programa}
		   GROUP BY 1,2,3,4,5
		   ORDER BY 1,2,3,4,5
		  ), cte_orgaos AS (
		    SELECT orgao_codigo as codigo, orgao_nome as nome, exercicio, SUM(valor) as valor
		      FROM cte_dados
		     GROUP BY 1,2,3
		  ), cte_unidades_orcamentarias AS (
		    SELECT unidade_orcamentaria_codigo as codigo, unidade_orcamentaria_nome as nome, exercicio, SUM(valor) as valor
		      FROM cte_dados
		     GROUP BY 1,2,3
		  ) SELECT codigo, nome, exercicio, valor FROM cte_orgaos
		    UNION ALL
		    SELECT codigo, nome, exercicio, valor FROM cte_unidades_orcamentarias
		    ORDER BY 1,2,3
		)
	end

	def join_programa
		if ppa.detalha_despesas_nas_iniciativas?
			join_programa_atraves_das_iniciativas
		else
			join_com_programa_diretamente
		end
	end

	def where_programa
		if ppa.detalha_despesas_nas_iniciativas?
			%Q(
				projecao.orcador_type = 'Ppa::Iniciativa'
			)
		else
			%Q(
				projecao.orcador_type = 'Ppa::Programa'
			)
		end
	end

	def join_programa_atraves_das_iniciativas
		%Q(
			JOIN ppa_iniciativas as iniciativa ON projecao.orcador_id = iniciativa.id
			JOIN ppa_objetivos as objetivo ON objetivo.id = iniciativa.objetivo_id
			JOIN ppa_programas as programa ON programa.id = objetivo.programa_id
		)
	end

	def join_com_programa_diretamente
		%Q(
			JOIN ppa_programas as programa ON projecao.orcador_id = programa.id
		)
	end
end
