package com.rjconsultores.ventaboletos.dao.hibernate;

import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.hibernate.type.LongType;
import org.hibernate.type.StringType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.rjconsultores.ventaboletos.dao.CtrlSerieEmbarcadaDAO;
import com.rjconsultores.ventaboletos.entidad.CtrlSerieEmbarcada;
import com.rjconsultores.ventaboletos.entidad.DispositivoEmbarcada;
import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.entidad.Estado;
import com.rjconsultores.ventaboletos.exception.SerieEmbarcadaNaoDisponivelException;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;
import com.rjconsultores.ventaboletos.vo.embarcada.SerieEmbarcada;

@Repository("ctrlSerieEmbarcadaDAO")
public class CtrlSerieEmbarcadaHibernateDAO  extends GenericHibernateDAO<CtrlSerieEmbarcada, Long> implements CtrlSerieEmbarcadaDAO {
	private static final Logger log = LogManager.getLogger(CtrlSerieEmbarcadaHibernateDAO.class);
			
	private static final Long MAX_NUMFOLIOSISTEMA_BPE = 999999999l;
	
    @Autowired
    public CtrlSerieEmbarcadaHibernateDAO(@Qualifier("sessionFactory") SessionFactory factory) {
        setSessionFactory(factory);
    }

	@Override
	@Transactional
	public CtrlSerieEmbarcada suscribir(CtrlSerieEmbarcada entity) {
		entity.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
		entity.setFecmodif(Calendar.getInstance().getTime());

		try {
			this.getHibernateTemplate().save(entity);
			getHibernateTemplate().flush();

			return entity;
		} catch (final HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
	}

	@Transactional
	@Override
	public void borrar(CtrlSerieEmbarcada entity) {
		entity.setActivo(Boolean.FALSE);
		entity.setIndBaixado(Boolean.TRUE);

		actualizacion(entity);
	}
	
	@Override
	@Transactional
	public CtrlSerieEmbarcada actualizacion(CtrlSerieEmbarcada entity) {
		entity.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
		entity.setFecmodif(Calendar.getInstance().getTime());
		
		entity = getHibernateTemplate().merge(entity);

		getHibernateTemplate().flush();
		
		return entity;
	}

	public Boolean validaDispositivoEmpresaEstadoCadastrado(DispositivoEmbarcada dispositivo, Empresa empresa, Estado estado) {
		StringBuilder sb = new StringBuilder();
		sb.append("select count(se.ctrlSerieEmbarcadaId) ");
		sb.append("from CtrlSerieEmbarcada se ");
		sb.append("where se.activo = 1 and se.indBaixado = 0 ");
		sb.append("and se.dispositivoEmbarcada.dispositivoEmbarcadaId = :dispositivoEmbarcadaId ");
		sb.append("and se.empresa.empresaId = :empresaId ");
		sb.append("and se.estado.estadoId = :estadoId ");

		Query qry = getSession().createQuery(sb.toString());
		qry.setLong("dispositivoEmbarcadaId", dispositivo.getDispositivoEmbarcadaId());
		qry.setLong("empresaId", empresa.getEmpresaId());
		qry.setLong("estadoId", estado.getEstadoId());

		Number n = (Number) qry.uniqueResult();
		
		if (n == null) {
			return false;
		}
		
		return n.longValue() > 0;
	}
	
	@SuppressWarnings("unchecked")
	public List<SerieEmbarcada> buscarSeriesEmbarcadaDisponivel(final Integer empresaId, final Integer estadoId) {
		StringBuilder sb = new StringBuilder();
		sb.append("select se.ctrlserieembarcada_id as ctrlSerieEmbarcadaId, se.serie as serie, se.numerocorrente + 1 as numero ");
		sb.append("from ctrl_serie_embarcada se ");
		sb.append("left join ctrl_serie_embarcada seq on seq.ctrlserieembarcadaseq_id = se.ctrlserieembarcada_id ");
		sb.append("where se.activo = 1 and se.ind_baixado = 1 and se.ind_reutilizado = 0 and seq.ctrlserieembarcada_id is null and se.empresa_id = :empresaId and se.estado_id = :estadoId ");
		sb.append("order by se.serie ");
		
		Query query = getSession().createSQLQuery(sb.toString())
				.addScalar("ctrlSerieEmbarcadaId", LongType.INSTANCE)
				.addScalar("serie", StringType.INSTANCE)
				.addScalar("numero", StringType.INSTANCE)
				.setResultTransformer(new AliasToBeanResultTransformer(SerieEmbarcada.class));
		
		query.setInteger("empresaId", empresaId);
		query.setInteger("estadoId", estadoId);
		
		return query.list();
	}
	
	@SuppressWarnings("unchecked")
	public CtrlSerieEmbarcada buscarCtrlSerieEmbarcada(final Long ctrlSerieEmbarcadaId) throws SerieEmbarcadaNaoDisponivelException {
		CtrlSerieEmbarcada retorno = null;
		
		StringBuilder sb = new StringBuilder();
		sb.append("select se ");
		sb.append("from CtrlSerieEmbarcada se ");
		sb.append("where se.activo = 1 and se.indReutilizado = 0 and se.ctrlSerieEmbarcadaId = :ctrlSerieEmbarcadaId");
		
		Session session = null;
		
		try {
			session = getSessionFactory().openSession();

			Query query = session.createQuery(sb.toString());		
			query.setLong("ctrlSerieEmbarcadaId", ctrlSerieEmbarcadaId);
			
			query.setMaxResults(1);
			query.setLockOptions(LockOptions.UPGRADE);
			query.setTimeout(20);
			
			List<CtrlSerieEmbarcada> list = query.list();
			
			if (list != null && list.size() > 0) {
				retorno = list.get(0);
				
				session.getTransaction().begin();

				retorno.setIndReutilizado(Boolean.TRUE);
				retorno.setFecmodif(new Date());
				
				session.update(retorno);
				
				session.getTransaction().commit();
			} else {
				throw new SerieEmbarcadaNaoDisponivelException();
			}
			
		} catch (SerieEmbarcadaNaoDisponivelException e) {
			throw new SerieEmbarcadaNaoDisponivelException();
		} catch (Exception e) {
			log.error("CtrlSerieEmbarcada: " + e.getMessage(), e);

		} finally {
			if (session != null) {
				session.flush();
				session.close();
			}
		}
		
		return retorno;
	}
	
	@SuppressWarnings("unchecked")
	public SerieEmbarcada buscarSerieEmbarcadaDisponivel(final Integer empresaId, final Integer estadoId) throws SerieEmbarcadaNaoDisponivelException {
		SerieEmbarcada retorno = null;
		
		StringBuilder sb = new StringBuilder();
		sb.append("select se ");
		sb.append("from CtrlSerieEmbarcada se ");
		sb.append("where se.indReutilizado = 0 and se.indBaixado = 1 and se.ctrlSerieEmbarcadaId in (:list)");
		sb.append("order by se.serie ");
		
		Set<Long> ids = buscarIdsSeriesEmbarcadaDisponivel(empresaId, estadoId);
		
		log.info("BuscarSerieEmbarcada :: IDs nao disponiveis");
		
		if (ids.isEmpty()) {
			throw new SerieEmbarcadaNaoDisponivelException();
		}
		
		log.info(String.format("BuscarSerieEmbarcada :: IDs teste disponivel %s" , ids.toString()));		
		
		Session session = null;
		
		try {
			session = getSessionFactory().openSession();

			Query query = session.createQuery(sb.toString());
			query.setParameterList("list", ids);
			
			query.setLockOptions(LockOptions.UPGRADE);
			query.setTimeout(20);
			
			List<CtrlSerieEmbarcada> list = query.list();
			
			if (list != null && list.size() > 0) {
				CtrlSerieEmbarcada ctrl = list.get(0);
				
				session.getTransaction().begin();

				ctrl.setIndReutilizado(Boolean.TRUE);
				ctrl.setFecmodif(new Date());
				
				session.update(ctrl);
				
				session.getTransaction().commit();
				
				retorno = new SerieEmbarcada();
				retorno.setCtrlSerieEmbarcada(ctrl);
				retorno.setSerie(ctrl.getSerie());
				
				Long numero = Long.valueOf(ctrl.getNumeroCorrente());
				
				if (!ctrl.getNumeroInicial().equals(ctrl.getNumeroCorrente())) {
					numero++;
				}
				
				log.info(String.format("BuscarSerieEmbarcada :: Numero encontrado [%d]", numero));
				
				if (numero > MAX_NUMFOLIOSISTEMA_BPE) {
					retorno = null;
				} else {
					retorno.setNumero(String.valueOf(numero));
				}
				
			} else {
				throw new SerieEmbarcadaNaoDisponivelException();
			}
			
		} catch (SerieEmbarcadaNaoDisponivelException e) {
			throw new SerieEmbarcadaNaoDisponivelException();
		} catch (Exception e) {
			log.error("CtrlSerieEmbarcada: " + e.getMessage(), e);
		} finally {
			if (session != null) {
				session.flush();
				session.close();
			}
		}
		
		return retorno;
	}
	
	@SuppressWarnings("unchecked")
	public Set<Long> buscarIdsSeriesEmbarcadaDisponivel(final Integer empresaId, final Integer estadoId) {
		StringBuilder sb = new StringBuilder();
		sb.append("select se.ctrlserieembarcada_id as ctrlSerieEmbarcadaId ");
		sb.append("from ctrl_serie_embarcada se ");
		sb.append("left join ctrl_serie_embarcada seq on seq.ctrlserieembarcadaseq_id = se.ctrlserieembarcada_id ");
		sb.append("where se.ind_baixado = 1 and se.ind_reutilizado = 0 and seq.ctrlserieembarcada_id is null and se.empresa_id = :empresaId and se.estado_id = :estadoId ");
		sb.append("order by se.serie ");
		
		Query query = getSession().createSQLQuery(sb.toString())
				.addScalar("ctrlSerieEmbarcadaId", LongType.INSTANCE);
		
		query.setInteger("empresaId", empresaId);
		query.setInteger("estadoId", estadoId);
		
		return new LinkedHashSet<Long>(query.list());
	}
	
	public Boolean validaPossuiDispositivo(Integer empresaId, Integer estadoId, String serie) {
		StringBuilder sql = new StringBuilder("")
				.append("SELECT COUNT(*) ")
				.append("FROM ctrl_serie_embarcada se ")
				.append("WHERE se.activo = 1 AND se.empresa_id = :empresaId AND se.estado_id = :estadoId AND se.serie = :serie");

		Query query = getSession().createSQLQuery(sql.toString());
		query.setInteger("empresaId", empresaId);
		query.setInteger("estadoId", estadoId);
		query.setString("serie", serie);

		Number n = (Number) query.uniqueResult();
		
		if (n == null) {
			return false;
		}
		
		return n.longValue() > 0;
	}	
}
