package com.rjconsultores.ventaboletos.dao.hibernate;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import com.rjconsultores.ventaboletos.dao.ConexionRutaConfDAO;
import com.rjconsultores.ventaboletos.entidad.ConexionCtrl;
import com.rjconsultores.ventaboletos.entidad.ConexionRutaConf;
import com.rjconsultores.ventaboletos.entidad.ConexionRutaCtrl;
import com.rjconsultores.ventaboletos.entidad.Parada;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.vo.conexion.OrdenarLocalidadesGeradas;
import com.rjconsultores.ventaboletos.vo.parada.ConexionCtrlVO;
import com.rjconsultores.ventaboletos.vo.parada.ConexionRutaConfVO;
import com.rjconsultores.ventaboletos.vo.parada.ConexionRutaCtrlVO;
import com.rjconsultores.ventaboletos.vo.parada.ParadaVOConexionRuta;

@Repository("conexionRutaConfDAO")
public class ConexionRutaConfHibernateDAO extends GenericHibernateDAO<ConexionRutaConf, Integer> implements ConexionRutaConfDAO {

	@Autowired
	public ConexionRutaConfHibernateDAO(@Qualifier("sessionFactory") SessionFactory factory) {
		setSessionFactory(factory);
	}

	@Override
	public List<ConexionRutaConf> buscarPorDescricao(String descricao) {

		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.ilike("descricao", descricao, MatchMode.ANYWHERE));

		return c.list();
	}

	@Override
	public List<ConexionRutaConf> obtenerTodosActivo() {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		return c.list();
	}

	@Override
	public void excluirConfiguracao(ConexionRutaConf conexion, Integer usuarioId, boolean excluirConexionRutaConf, boolean isConexionGerada) {
		List<String> comandos = carregarComandosInativarConfiguracao(excluirConexionRutaConf, isConexionGerada);
		for (String comando : comandos) {
			Query qr = getSession().createSQLQuery(comando);
			qr.setParameter("usuarioId", usuarioId);
			qr.setParameter("conexionrutaconfId", conexion.getConexionRutaConfId());
			qr.setParameter("fecmodif", Calendar.getInstance().getTime());
			qr.executeUpdate();
		}
	}
	
	private List<String> carregarComandosInativarConfiguracao(boolean excluirConexionRutaConf, boolean isConexionGerada) {
		List<String> comandos = new ArrayList<String>(0);
		
		StringBuilder sb = new StringBuilder();
		if(excluirConexionRutaConf) {
			sb.append("update conexion_ruta_conf ")
			  .append("set activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ")
			  .append("where conexionrutaconf_id = :conexionrutaconfId ")
			  .append("and activo = 1");
			comandos.add(sb.toString());
		}
		
		sb = new StringBuilder();
		sb.append("update conexion_ruta_excepcion_ptovta ")
		  .append("set activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ")
		  .append("where conexionrutaconf_id = :conexionrutaconfId ")
		  .append("and activo = 1");
		comandos.add(sb.toString());
		
		sb = new StringBuilder();
		sb.append("update conexion_rutaexcepciontipopta ")
		  .append("set activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ")
		  .append("where conexionrutaconf_id = :conexionrutaconfId ")
		  .append("and activo = 1");
		comandos.add(sb.toString());
	
		if(isConexionGerada || excluirConexionRutaConf) {
			sb = new StringBuilder();
			sb.append("update conexion_ruta_ctrl ")
			   .append("set activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ")
			   .append("where conexionrutaconf_id = :conexionrutaconfId ")
			   .append("and activo = 1");
			comandos.add(sb.toString());
	
			sb = new StringBuilder();
			sb.append("merge into conexion_ruta_tramo_ctrl c ")
			  .append("using ( ")
			  .append("select c1.conexionrutatramoctrl_id ")
			  .append("from conexion_ruta_tramo_ctrl c1 ")
			  .append("join conexion_ruta_ctrl c2 on c1.conexionrutactrl_id = c2.conexionrutactrl_id ")
			  .append("where c2.conexionrutaconf_id = :conexionrutaconfId and c1.activo = 1) t on (t.conexionrutatramoctrl_id = c.conexionrutatramoctrl_id) ")
			  .append("when matched then update set c.activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ");
			comandos.add(sb.toString());
	
			sb = new StringBuilder();
			sb.append("merge into conexion c ")
			  .append("using ( ")
			  .append("select c.conexion_id ")
			  .append("from conexion c ")
			  .append("join conexion_ruta_tramo_ctrl c1 on c.conexionrutatramoctrl_id = c1.conexionrutatramoctrl_id ")
			  .append("join conexion_ruta_ctrl c2 on c1.conexionrutactrl_id = c2.conexionrutactrl_id ")
			  .append("where c2.conexionrutaconf_id = :conexionrutaconfId and c.activo = 1) t on (t.conexion_id = c.conexion_id) ")
			  .append("when matched then update set c.activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif");
			comandos.add(sb.toString());
	
			sb = new StringBuilder();
			sb.append("merge into conexion_ctrl c ")
			  .append("using ( ")
			  .append("select distinct c.conexionctrl_id ")
			  .append("from conexion_ctrl c ")
			  .append("join conexion c3 on c3.conexionctrl_id = c.conexionctrl_id ")
			  .append("join conexion_ruta_tramo_ctrl c1 on c3.conexionrutatramoctrl_id = c1.conexionrutatramoctrl_id ")
			  .append("join conexion_ruta_ctrl c2 on c1.conexionrutactrl_id = c2.conexionrutactrl_id ")
			  .append("where c2.conexionrutaconf_id = :conexionrutaconfId and c.activo = 1) t on (t.conexionctrl_id = c.conexionctrl_id) ")
			  .append("when matched then update set c.activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif");
			comandos.add(sb.toString());
	
			sb = new StringBuilder();
			sb.append("merge into conexion_conf c ")
			  .append("using ( ")
			  .append("select distinct c.conexionconf_id ")
			  .append("from conexion_conf c ")
			  .append("join conexion_ctrl c4 on c4.conexionctrl_id = c.conexionctrl_id ")
			  .append("join conexion c3 on c3.conexionctrl_id = c4.conexionctrl_id ")
			  .append("join conexion_ruta_tramo_ctrl c1 on c3.conexionrutatramoctrl_id = c1.conexionrutatramoctrl_id ")
			  .append("join conexion_ruta_ctrl c2 on c1.conexionrutactrl_id = c2.conexionrutactrl_id ")
			  .append("where c2.conexionrutaconf_id = :conexionrutaconfId and c.activo = 1) t on (t.conexionconf_id = c.conexionconf_id) ")
			  .append("when matched then update set c.activo = 0, usuario_id = :usuarioId, fecmodif = :fecmodif ");
			comandos.add(sb.toString());
		}
		
		return comandos;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ConexionRutaConfVO carregarConexoesCadastradas(Integer conexionrutaconfId) {
		ConexionRutaConfVO retorno = new ConexionRutaConfVO();
		
		if(conexionrutaconfId == null) {
			return retorno;
		}
		
		StringBuilder sQuery = new StringBuilder();
		sQuery.append(getSQLCarregarConexoesCadastradas())
			  .append(" where ctrl.activo = 1 ")
			  .append(" and conf.conexionrutaconf_id = :conexionrutaconfId ");
		
		Query qr = getSession().createSQLQuery(sQuery.toString());
		qr.setParameter("conexionrutaconfId", conexionrutaconfId);
		
		List<ParadaVOConexionRuta> localidadesCombinadas = new ArrayList<ParadaVOConexionRuta>(0);
		List<ConexionCtrl> listConexionCtrl = new ArrayList<ConexionCtrl>(0);		
		List<ConexionCtrlVO> listConexionCtrlVO = new ArrayList<ConexionCtrlVO>(0);
		
		List<Object[]> result = qr.list();
		
		transformarObjetosConexao(localidadesCombinadas, listConexionCtrl, listConexionCtrlVO, result);
		
		retorno.setListConexionCtrl(listConexionCtrl);
		
		Collections.sort(localidadesCombinadas, new OrdenarLocalidadesGeradas());
		retorno.setLocalidadesCombinadas(localidadesCombinadas);
		
		return retorno;
	}

	@Override
	public void transformarObjetosConexao(List<ParadaVOConexionRuta> localidadesCombinadas,
			List<ConexionCtrl> listConexionCtrl, List<ConexionCtrlVO> listConexionCtrlVO, List<Object[]> result) {
		for (Object[] tupla : result) {
			Long conexionRutaTramoId = Long.valueOf(tupla[0].toString());
			Integer rutaId = Integer.valueOf(tupla[1].toString());
			String numRuta = tupla[2].toString();
			String sentido = tupla[3].toString();
			
			Parada paradaOrigem = new Parada(Integer.valueOf(tupla[4].toString()), tupla[5].toString(), tupla[6].toString());
			Parada paradaDestino = new Parada(Integer.valueOf(tupla[7].toString()), tupla[8].toString(), tupla[9].toString());
			Parada paradaOrigemTrecho = new Parada(Integer.valueOf(tupla[10].toString()), tupla[11].toString(), tupla[12].toString());
			Parada paradaDestinoTrecho = new Parada(Integer.valueOf(tupla[13].toString()), tupla[14].toString(), tupla[15].toString());
			Integer grupo = Integer.valueOf(tupla[16].toString());
			Short secuencia = Short.valueOf(tupla[17].toString());
			
			Integer rutaOrigemId = Integer.valueOf(tupla[18].toString());
			Integer rutaDestinoId = Integer.valueOf(tupla[19].toString());
			
			Integer rutaDestinoCId = null;
			if(tupla[20] != null) {
				rutaDestinoCId = Integer.valueOf(tupla[20].toString());
			}
			
			Long conexionctrlId =  Long.valueOf(tupla[21].toString());
			Boolean indPricing =  tupla[22] == null ? false : Integer.valueOf(tupla[22].toString()) == 1;
			
			ConexionCtrlVO conexionCtrl = recuperarConexionCtrlVO(listConexionCtrlVO, listConexionCtrl, paradaOrigemTrecho.getParadaId(), paradaDestinoTrecho.getParadaId(), conexionctrlId, indPricing, grupo);
			ConexionRutaCtrlVO conexionRutaCtrl = new ConexionRutaCtrlVO(rutaOrigemId, rutaDestinoId, rutaDestinoCId);
			
			ParadaVOConexionRuta trecho = new ParadaVOConexionRuta(grupo, paradaOrigem, paradaDestino, rutaId, numRuta, secuencia, true, paradaOrigemTrecho, paradaDestinoTrecho, conexionCtrl, conexionRutaCtrl, sentido);
			trecho.setConexionRutaTramoId(conexionRutaTramoId);
			
			localidadesCombinadas.add(trecho);
		}
	}
	
	@Override
	public String getSQLCarregarConexoesCadastradas() {
		StringBuilder sQuery = new StringBuilder();
		sQuery.append("select ctrl.conexionrutatramoctrl_id conexionrutatramoctrlId, r.ruta_id rutaId, r.numruta, ")
			  .append("case when coalesce(r.indsentidoida,1) = 1 then 'IDA' else 'VOLTA' end sentido, ")
			  .append("ori.parada_id paradaIdOri, ori.cveparada cveparadaOri, ori.descparada descparadaOri, ")
			  .append("des.parada_id paradaIdDes, des.cveparada cveparadaDes, des.descparada descparadaDes, ")
			  .append("oriT.parada_id paradaIdOriT, oriT.cveparada cveparadaOriT, oriT.descparada descparadaOriT, ")
			  .append("desT.parada_id paradaIdDesT, desT.cveparada cveparadaDesT, desT.descparada descparadaDesT, ")
			  .append("co.numgrupo, co.numsecuencia, ctr.ruta_origen_id rutaOrigenId, ctr.ruta_destino_id rutaDestinoId, ")
			  .append("ctr.ruta_destinoc_id rutaDestinoCId, coctrl.conexionctrl_id, coctrl.indPricing ")
			  .append("from conexion_ruta_tramo_ctrl ctrl ")
			  .append("join ruta r on r.ruta_id = ctrl.ruta_id ")
			  .append("join conexion_ruta_ctrl ctr on ctr.conexionrutactrl_id = ctrl.conexionrutactrl_id ")
			  .append("join conexion_ruta_conf conf on conf.conexionrutaconf_id = ctr.conexionrutaconf_id ")
			  .append("join conexion co on co.conexionrutatramoctrl_id = ctrl.conexionrutatramoctrl_id and co.activo = 1 ")
			  .append("join conexion_ctrl coctrl on coctrl.conexionctrl_id = co.conexionctrl_id and co.activo = 1 ")
			  .append("join parada ori on ori.parada_id = ctrl.origen_id ")
			  .append("join parada des on des.parada_id = ctrl.destino_id ")
			  .append("join parada oriT on oriT.parada_id = coctrl.origen_id ")
			  .append("join parada desT on desT.parada_id = coctrl.destino_id ");
		
		return sQuery.toString();
	}

	private ConexionCtrlVO recuperarConexionCtrlVO(List<ConexionCtrlVO> listConexionCtrlVO, List<ConexionCtrl> listConexionCtrl, Integer origemConexaoCtrl, Integer destinoConexaoCtrl, Long conexionctrlId, Boolean indPricing, Integer grupo) {
		for (ConexionCtrlVO conexionCtrlTemp : listConexionCtrlVO) {
			if(conexionCtrlTemp.isOrigemDestinoGrupoIgual(origemConexaoCtrl, destinoConexaoCtrl, grupo)) {
				return conexionCtrlTemp;
			}
		}
		
		ConexionCtrlVO conexionCtrlVO = new ConexionCtrlVO(origemConexaoCtrl, destinoConexaoCtrl);
		conexionCtrlVO.setConexionctrlId(conexionctrlId);
		conexionCtrlVO.setIndPricing(indPricing);
		conexionCtrlVO.setGrupo(grupo);
		listConexionCtrlVO.add(conexionCtrlVO);
		
		ConexionCtrl conexaoCtrl = new ConexionCtrl(origemConexaoCtrl, destinoConexaoCtrl, grupo);
		conexaoCtrl.setConexionctrlId(conexionctrlId);
		conexaoCtrl.setIndPricing(indPricing);
		listConexionCtrl.add(conexaoCtrl);
		
		return conexionCtrlVO;
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<List<Ruta>> buscarRutasPorConexionConf(Integer conexionRutaConfId) {
		StringBuilder sQuery = new StringBuilder("SELECT ctrl ");
		sQuery.append("FROM ConexionRutaCtrl ctrl ")
			  .append("WHERE ctrl.activo = 1 ")
			  .append("AND ctrl.conexionRutaConf.conexionRutaConfId = :conexionRutaConfId ");
		
		
		Query qr = getSession().createQuery(sQuery.toString());
		qr.setParameter("conexionRutaConfId", conexionRutaConfId);
		List<ConexionRutaCtrl> result = qr.list();
		
		List<Ruta> lsRutaOrigem = new ArrayList<Ruta>();
		List<Ruta> lsRutaDestino = new ArrayList<Ruta>();
		List<Ruta> lsRutaDestinoC = new ArrayList<Ruta>();
		
		for (ConexionRutaCtrl conexionRutaCtrl : result) {
			if(!lsRutaOrigem.contains(conexionRutaCtrl.getRutaOrigen())) {
				lsRutaOrigem.add(conexionRutaCtrl.getRutaOrigen());
			}
			
			if(!lsRutaDestino.contains(conexionRutaCtrl.getRutaDestino())) {
				lsRutaDestino.add(conexionRutaCtrl.getRutaDestino());
			}
			
			if(conexionRutaCtrl.getRutaDestinoC() != null && !lsRutaDestinoC.contains(conexionRutaCtrl.getRutaDestinoC())) {
				lsRutaDestinoC.add(conexionRutaCtrl.getRutaDestinoC());
			}
		}
		
		List<List<Ruta>> retorno = new ArrayList<List<Ruta>>();
		retorno.add(lsRutaOrigem);
		retorno.add(lsRutaDestino);
		retorno.add(lsRutaDestinoC);
		
		return retorno;
		
	}

	@Override
	public void atualizarTempoMinimoEMaximo(ConexionRutaConf conexionRutaConf) {
		StringBuilder sb  = new StringBuilder();
		sb.append("merge into conexion_conf c ")
		  .append("using ( ")
		  .append("select distinct c.conexionconf_id ")
		  .append("from conexion_conf c ")
		  .append("join conexion_ctrl c4 on c4.conexionctrl_id = c.conexionctrl_id ")
		  .append("join conexion c3 on c3.conexionctrl_id = c4.conexionctrl_id ")
		  .append("join conexion_ruta_tramo_ctrl c1 on c3.conexionrutatramoctrl_id = c1.conexionrutatramoctrl_id ")
		  .append("join conexion_ruta_ctrl c2 on c1.conexionrutactrl_id = c2.conexionrutactrl_id ")
		  .append("where c2.conexionrutaconf_id = :conexionrutaconfId and c.activo = 1) t on (t.conexionconf_id = c.conexionconf_id) ")
		  .append("when matched then update set usuario_id = :usuarioId, fecmodif = :fecmodif, tiempomin = :tiempomin, tiempomax = :tiempomax ");
		
		Query qr = getSession().createSQLQuery(sb.toString());
		qr.setParameter("usuarioId", conexionRutaConf.getUsuarioId());
		qr.setParameter("conexionrutaconfId", conexionRutaConf.getConexionRutaConfId());
		qr.setParameter("fecmodif", Calendar.getInstance().getTime());
		qr.setParameter("tiempomin", conexionRutaConf.getTiempoMin());
		qr.setParameter("tiempomax", conexionRutaConf.getTiempoMax());
		qr.executeUpdate();
	}

	@Override
	public ConexionRutaConf getByIdActivo(Integer conexionRutaConfId) {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.eq("conexionRutaConfId", conexionRutaConfId));
		return (ConexionRutaConf) c.uniqueResult();
	}

}
