/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rjconsultores.ventaboletos.web.utilerias;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.flywaydb.core.Flyway;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.zkoss.lang.Library;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.WebApp;

import com.rjconsultores.ventaboletos.FlyWay;
import com.rjconsultores.ventaboletos.entidad.Constante;
import com.rjconsultores.ventaboletos.service.ConstanteService;
import com.rjconsultores.ventaboletos.service.EmpresaService;
import com.rjconsultores.ventaboletos.utilerias.ApplicationProperties;
import com.rjconsultores.ventaboletos.web.gui.controladores.job.GeneracionConferenciaMovimentoJob;
import com.rjconsultores.ventaboletos.web.gui.controladores.job.GeneracionCorridaJob;
import com.rjconsultores.ventaboletos.web.gui.controladores.job.GeneracionRetencaoDiariaComissaoJob;
import com.rjconsultores.ventaboletos.web.gui.controladores.job.IntegracaoSapJob;
import com.rjconsultores.ventaboletos.web.gui.controladores.job.MonitoramentoCCFCRZJob;
import com.rjconsultores.ventaboletos.web.utilerias.spring.AppContext;

/**
 * 
 * @author gleimar
 */
public class MyAppInit implements org.zkoss.zk.ui.util.WebAppInit {

	private static final String HORA_GENERACION_CORRIDA = "HORA_GENERACION_CORRIDA";
	private static final String HORA_EXEC_CONFERENCIA_MOVIMENTO = "HORA_EXEC_CONFERENCIA_MOVIMENTO";
	private static final String HORA_EXEC_MONITORAMENTO_REDUCAOZ = "HORA_EXEC_MONITORAMENTO_REDUCAOZ";
	private static final String HORA_INTEGRACAO_SAP = "HORA_INTEGRACAO_SAP";
	final private static String AMBIENTE_DEV = "dev";
	final private static String AMBIENTE_CONSULTA = "consulta";
	private static Logger log = LogManager.getLogger(MyAppInit.class);
	SchedulerFactory schedFact = new StdSchedulerFactory();
	Scheduler sched;

	public Scheduler getSched() {
		return sched;
	}

	public void setSched(Scheduler sched) {
		this.sched = sched;
	}

	public SchedulerFactory getSchedFact() {
		return schedFact;
	}

	public void setSchedFact(SchedulerFactory schedFact) {
		this.schedFact = schedFact;
	}

	@Override
	public void init(WebApp wapp) throws Exception {
		Labels.register(new MyLabelLocatorGeneral((ServletContext) wapp.getNativeContext()));
//		Labels.register(new MyLabelLocatorCliente((ServletContext) wapp.getNativeContext()));

		//Definição do comportamento padrão do Datebox em relação ao formato de data
		//Mais informações em -> https://www.zkoss.org/wiki/ZK_Developer's_Reference/Internationalization/Date_and_Time_Formatting
		Library.setProperty("org.zkoss.web.preferred.dateFormatInfo", "com.rjconsultores.ventaboletos.web.utilerias.MyDateFormatInfo");
		
		boolean ipBloqueadoGeracaoServico = ipBloqueadoGeracaoServico();

		log.info("ipBloqueadoGeracaoServico=" + ipBloqueadoGeracaoServico);

		if (!ipBloqueadoGeracaoServico) {
			log.info("ip liberado");

			// Generacion Automatica de Corridas
			jobGeneracionCorridas();
			
			//geração automatica da conferência de mmovimento.
			jobGeneracionConferenciaMovimento();
			//Monitoramento automático de Quebra de sequência na redução Z.
			jobMonitoramentoReducaoZ();
			
			//Retencao diara comissao
			jobGeneracionRetencaoDiariaComissao();
			
			jobIntegracaoSap();
		}
		
		String ambiente = System.getProperty("ambiente") ==null || System.getProperty("ambiente").isEmpty()  ?  "" : System.getProperty("ambiente");
		if( !AMBIENTE_DEV.equals(ambiente.toLowerCase()) && !AMBIENTE_CONSULTA.equals(ambiente.toLowerCase()) ) {
			executeFlyway();
			customToDatabase();
			imageToDatabase();
		}
		
		this.atualizarLicencaPrimeiraVez();
	}
	

	/**
	 * Executa os sqls novos na base de dados
	 */
	private void executeFlyway() {
		FlyWay.getInstance().defineDataSource(getDataSoure());
		FlyWay.getInstance().start();
		
		log.info("Executando Flyway...");
		
		try{
			final Flyway flyway = new Flyway();
	
			flyway.setDataSource(getDataSoure());
			flyway.setValidateOnMigrate(false);
			flyway.setIgnoreFutureMigrations(true);
			flyway.setOutOfOrder(true);
			flyway.setBaselineOnMigrate(true);
	
			flyway.migrate();
		}catch(Throwable t){
			log.error("Erro ao executar o flyway",t);
		}
		
		log.info("Flyway executado.");
	}
	
	private void customToDatabase() {
		
		log.info("Executando customToDatabase...");
		ApplicationProperties.getInstance().readConfigurationToDatabase();
	}
	
	private void imageToDatabase() {
		
		log.info("Executando loadImageToDatabase...");
		ApplicationProperties.getInstance().loadImageToDatabase("empresa.png", false);
		ApplicationProperties.getInstance().loadImageToDatabase("empresa_generica.png", true);
		ApplicationProperties.getInstance().singletonImage();
	}
	
	/**
	 * Se nenhuma das constantes abaixo estiver configurada, o serviço de geração é iniciado.
	 * 
	 * SERVIDOR_GENERACION_CORRIDA_EXCLUSIVO : Tem o IP aonde será liberado a geração de corrida. Somente nesse IP
	 * 
	 * SERVIDOR_EXCEPCION_GENERACION_CORRIDA: Tem o IP aonde não será gerado a corrida. Somente esse IP
	 * 
	 * 	 * @return
	 */
	private boolean ipBloqueadoGeracaoServico() {
		
		Constante constanteServidorExclusivo= getConstanteService().buscarPorNomeConstante("SERVIDOR_GENERACION_CORRIDA_EXCLUSIVO");
		Constante constanteServidorException = getConstanteService().buscarPorNomeConstante("SERVIDOR_EXCEPCION_GENERACION_CORRIDA");
		
		String ipConstanteExcepcion = constanteServidorException == null ? "" : constanteServidorException.getValorconstante();
		String ipConstanteExclusivo = constanteServidorExclusivo == null ? "" : constanteServidorExclusivo.getValorconstante();

		log.info(" IP Constante Excepcion: " + ipConstanteExcepcion);
		log.info(" IP Constante Exclusivo: " + ipConstanteExclusivo);
		
		if (StringUtils.isBlank(ipConstanteExclusivo) && StringUtils.isBlank(ipConstanteExcepcion)){
			return false;
		}		
		
		List<String> lsIps = getListIp();
		
		if (StringUtils.isNotBlank(ipConstanteExclusivo)){
			if (lsIps.contains(ipConstanteExclusivo)) {
				log.info(" IP N�o bloqueado. Liberado exclusivamente. ");

				return false;
			}else{
				log.info(" IP bloqueado. N�o � o ip exclusivamente liberado. ");
				
				return true;
			}
		}
		
		if (StringUtils.isNotBlank(ipConstanteExcepcion)){
			if (lsIps.contains(ipConstanteExcepcion)) {
				log.info(" IP bloqueado. Bloqueado por excecao. ");

				return true;
			} 
		}
		
		return false;
	}

	private List<String> getListIp() {
		List<String> lsIps = new ArrayList<String>();
				
		String ipExternalAWS = IPValidatorUtileria.getIpExternalAWS();
		log.info("ipExternalAWS: "+ipExternalAWS);
		
		String ip = IPValidatorUtileria.getIp();
		log.info("ip: "+ip);
		
		
		if (ipExternalAWS!=null){lsIps.add(ipExternalAWS);}
		if (ip!=null){lsIps.add(ip);}
		return lsIps;
	}

	private Integer[] horaExecucion(String nombConstante) {

		Constante constante = getConstanteService().buscarPorNomeConstante(nombConstante);

		Integer[] arrayHoraMinuto = new Integer[2];
		arrayHoraMinuto[0] = 1;
		arrayHoraMinuto[1] = 0;

		if ((constante == null) || (constante.getValorconstante() == null)) {
			return null;
		}

		int hora = 1;
		int minuto = 0;
		try {
			String[] vlrContante = constante.getValorconstante().split(":");

			hora = Integer.parseInt(vlrContante[0]);
			minuto = Integer.parseInt(vlrContante[1]);
			if (hora < 0 || hora > 23) {
				hora = 1;
			}
			if (minuto < 0 || minuto > 59) {
				minuto = 0;
			}

			arrayHoraMinuto[0] = hora;
			arrayHoraMinuto[1] = minuto;

		} catch (Exception e) {
			log.error("Param hora geracion corrida invalido : " + constante.getValorconstante());
			arrayHoraMinuto[0] = 1;
			arrayHoraMinuto[1] = 0;
		}

		return arrayHoraMinuto; 
	}

	private void jobGeneracionCorridas() {
		try {
			sched = schedFact.getScheduler();

			JobDetail jobDetail = new JobDetail("GeneracionCorridas", null, GeneracionCorridaJob.class);

			Integer[] hora = horaExecucion(HORA_GENERACION_CORRIDA);

			if (hora == null) {
				log.info("constante HORA_GENERACION_CORRIDA Não habilitada.");

				return;
			}

			log.info("Horario de geracao de corrida : " + hora[0] + ":" + hora[1]);

			Trigger trigger = TriggerUtils.makeDailyTrigger("generacionCorridasTrigger", hora[0], hora[1]);

			// TESTE TRIGGER
			// Trigger trigger = TriggerUtils.makeImmediateTrigger("generacionCorridasTrigger", 1, 1000);

			trigger.setName("generacionCorridasTrigger");

			JobDetail job = sched.getJobDetail(jobDetail.getName(), jobDetail.getGroup());

			log.info("Job=" + jobDetail.getName() + "." + jobDetail.getGroup());

			if (job != null) {
				log.info("Job ya existia");
				boolean deleted = sched.deleteJob(job.getName(), Scheduler.DEFAULT_GROUP);
				log.info("Deleted=" + deleted);
			}

			sched.scheduleJob(jobDetail, trigger);

			sched.start();

		} catch (SchedulerException ex) {
			log.error(ex);
		}
	}
	
	private void jobIntegracaoSap() {
		try {
			sched = schedFact.getScheduler();

			JobDetail jobDetail = new JobDetail("IntegracaoSap", null, IntegracaoSapJob.class);

			Integer[] hora = horaExecucion(HORA_INTEGRACAO_SAP);

			if (hora == null) {
				log.info("constante HORA_INTEGRACAO_SAP Não habilitada.");

				return;
			}

			log.info("Horario de integração com sap : " + hora[0] + ":" + hora[1]);

			Trigger trigger = TriggerUtils.makeDailyTrigger("integracaoSapTrigger", hora[0], hora[1]);

			// TESTE TRIGGER
//			 Trigger trigger = TriggerUtils.makeImmediateTrigger("integracaoSapTrigger", 1, 1000);

			trigger.setName("integracaoSapTrigger");

			JobDetail job = sched.getJobDetail(jobDetail.getName(), jobDetail.getGroup());

			log.info("Job=" + jobDetail.getName() + "." + jobDetail.getGroup());

			if (job != null) {
				log.info("Job ya existia");
				boolean deleted = sched.deleteJob(job.getName(), Scheduler.DEFAULT_GROUP);
				log.info("Deleted=" + deleted);
			}

			sched.scheduleJob(jobDetail, trigger);

			sched.start();

		} catch (SchedulerException ex) {
			log.error(ex);
		}
	}
	
	/**
	 * Cria job do Quartz para Conferencia de Movimento Automatica do dia anterior.
	 */
	private void jobGeneracionConferenciaMovimento() {
		try {
			Integer[] hora = horaExecucion(HORA_EXEC_CONFERENCIA_MOVIMENTO);
			if(hora ==null){
				log.info("Não existe hora cadastrada para o job GeneracionConferenciaMovimentoJob. Verificar a constante HORA_EXEC_CONFERENCIA_MOVIMENTO");
				return;
			}
			log.info("Início cadastro do job GeneracionConferenciaMovimentoJob");
			sched = schedFact.getScheduler();
			JobDetail jobDetail = new JobDetail("GeneracionConferenciaMovimentoJob", null, GeneracionConferenciaMovimentoJob.class);
			Trigger trigger = TriggerUtils.makeDailyTrigger("generacionConferenciaMovimentoTrigger", hora[0], hora[1]);
			log.info("GeneracionConferenciaMovimentoJob hora execucão: " + hora[0] + ":" + hora[1] );
			// TESTE TRIGGER
			//Trigger trigger = TriggerUtils.makeImmediateTrigger("generacionConferenciaMovimentoTrigger", 1, 1000);
			trigger.setName("generacionConferenciaMovimentoTrigger");
			JobDetail job = sched.getJobDetail(jobDetail.getName(), jobDetail.getGroup());
			log.info("Job=" + jobDetail.getName() + "." + jobDetail.getGroup());
			if (job != null) {
				log.info("Job já existe");
				boolean deleted = sched.deleteJob(job.getName(), Scheduler.DEFAULT_GROUP);
				log.info("Deleted=" + deleted);
			}
			sched.scheduleJob(jobDetail, trigger);
			sched.start();
			log.info("Fim cadastro do job GeneracionConferenciaMovimentoJob");
		} catch (SchedulerException ex) {
			log.error(ex);
		}
	}
	
	/**
	 * Cria job do Quartz para Conferência de Movimento automática do dia anterior.
	 */
	private void jobMonitoramentoReducaoZ() {
		try {
			Integer[] hora = horaExecucion(HORA_EXEC_MONITORAMENTO_REDUCAOZ);
			if(hora ==null){
				log.info("Não existe hora cadastrada para o job MonitoramentoCCFCRZJob. Verificar a constante HORA_EXEC_MONITORAMENTO_REDUCAOZ");
				return;
			}
			log.info("Início cadastro do job MonitoramentoCCFCRZJob");
			sched = schedFact.getScheduler();
			JobDetail jobDetail = new JobDetail("MonitoramentoCCFCRZJob", null, MonitoramentoCCFCRZJob.class);
			Trigger trigger = TriggerUtils.makeDailyTrigger("monitoramentoCCFCRZJobTrigger", hora[0], hora[1]);
			log.info("MonitoramentoCCFCRZJob hora execucão: " + hora[0] + ":" + hora[1] );
			trigger.setName("monitoramentoCCFCRZJobTrigger");
			JobDetail job = sched.getJobDetail(jobDetail.getName(), jobDetail.getGroup());
			log.info("Job=" + jobDetail.getName() + "." + jobDetail.getGroup());
			if (job != null) {
				log.info("Job já existe");
				boolean deleted = sched.deleteJob(job.getName(), Scheduler.DEFAULT_GROUP);
				log.info("Deleted=" + deleted);
			}
			sched.scheduleJob(jobDetail, trigger);
			sched.start();
			log.info("Fim cadastro do job MonitoramentoCCFCRZJob");
		} catch (SchedulerException ex) {
			log.error(ex);
		}
	}
	
	/**
	 * Cria job do Quartz para Retencao Diarria da Comissao, sempre executada para o dia anterior.
	 */
	private void jobGeneracionRetencaoDiariaComissao() {
		try {
			log.info("Início cadastro do job GeneracionRetencaoDiariaComissaoJob");
			sched = schedFact.getScheduler();
			//Trigger trigger = TriggerUtils.makeMinutelyTrigger("generacionRetencaoDiariaComissaoTrigger", 5, 5);
			Trigger trigger = TriggerUtils.makeHourlyTrigger("generacionRetencaoDiariaComissaoTrigger");
			JobDetail jobDetail = new JobDetail("GeneracionRetencaoDiariaComissaoJob", null, GeneracionRetencaoDiariaComissaoJob.class);
			log.info("GeneracionRetencaoDiariaComissaoJob em execucão a cada hora");
			trigger.setName("generacionRetencaoDiariaComissaoTrigger");
			JobDetail job = sched.getJobDetail(jobDetail.getName(), jobDetail.getGroup());
			log.info("Job=" + jobDetail.getName() + "." + jobDetail.getGroup());
			if (job != null) {
				log.info("Job já existe");
				boolean deleted = sched.deleteJob(job.getName(), Scheduler.DEFAULT_GROUP);
				log.info("Deleted=" + deleted);
			}
			sched.scheduleJob(jobDetail, trigger);
			sched.start();
			log.info("Fim cadastro do job GeneracionRetencaoDiariaComissaoJob");
		} catch (SchedulerException ex) {
			log.error(ex);
		}
	}	
	
	private DataSource getDataSoure() {
		ApplicationContext appContext = AppContext.getApplicationContext();
		BeanFactory factory = (BeanFactory) appContext;
		DataSource ds = (DataSource) factory.getBean("dataSource");
		return ds;
	}
	
	private ConstanteService getConstanteService() {
		ApplicationContext appContext = AppContext.getApplicationContext();
		BeanFactory factory = (BeanFactory) appContext;
		ConstanteService cs = (ConstanteService) factory.getBean("constanteService");
		return cs;
	}
	
	private void atualizarLicencaPrimeiraVez(){
		ApplicationContext appContext = AppContext.getApplicationContext();
		BeanFactory factory = (BeanFactory) appContext;
		EmpresaService es = (EmpresaService) factory.getBean("empresaService");
		
		Integer cantLicencaEmpresasPrimeiraVez = es.atualizarLicencaEmpresasPrimeiraVez();
		
		log.info(String.format("cantLicencaEmpresasPrimeiraVez: %s", cantLicencaEmpresasPrimeiraVez));
	}
}
