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

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.BooleanType;
import org.hibernate.type.IntegerType;
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.EmpresaDAO;
import com.rjconsultores.ventaboletos.entidad.ComEmpCategoria;
import com.rjconsultores.ventaboletos.entidad.ComEmpConferencia;
import com.rjconsultores.ventaboletos.entidad.ComEmpFormapago;
import com.rjconsultores.ventaboletos.entidad.ComEmpTipoEventoExtra;
import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.entidad.InscricaoEstadual;
import com.rjconsultores.ventaboletos.entidad.PuntoVenta;
import com.rjconsultores.ventaboletos.enums.FolioSistemaEnum;
import com.rjconsultores.ventaboletos.vo.busquedapacotes.transformer.DatosEmpresaResultTransformer;

/**
 *
 * @author Administrador
 */
@Repository("empresaDAO")
public class EmpresaHibernateDAO extends GenericHibernateDAO<Empresa, Integer> implements EmpresaDAO {
	
	
	private static String FS_BPE = "FS_BPE_";
	private static Logger log = LogManager.getLogger(EmpresaHibernateDAO.class);

	@Autowired
	private DataSource dataSource;

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

	@Override
	public List<Empresa> obtenerTodos() {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.ne("empresaId", -1));
		c.addOrder(Order.asc("nombempresa"));
		
		@SuppressWarnings("unchecked")
		List<Empresa> retorno =(List<Empresa>) c.list();
		
		for(Empresa empresa : retorno) {
			Hibernate.initialize(empresa);
			Hibernate.initialize(empresa.getInscricoesEstaduais());
		}
		
		return retorno;
	}

	@SuppressWarnings("unchecked")
	public List<Empresa> buscar(String nombempresa, Boolean indExterna, Short indTipo) {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.eq("nombempresa", nombempresa));
		c.add(Restrictions.eq("indExterna", indExterna));
		c.add(Restrictions.eq("indTipo", indTipo));
		c.add(Restrictions.ne("empresaId", -1));

		return c.list();
	}

	public List<Empresa> buscarTodosExceto(List<Empresa> empresa, Integer... idEmpresa) {
		if (idEmpresa == null || idEmpresa.length == 0) {
			return empresa;
		}

		List<Empresa> empresaList = new ArrayList<Empresa>();
		List<Integer> lsEmpresa = Arrays.asList(idEmpresa);
		for (Empresa e : empresa) {
			if (!lsEmpresa.contains(e.getEmpresaId())) {
				empresaList.add(e);
			}
		}

		return empresaList;

	}

	/**
	 * Indica se está no momento em que nenhuma empresa tem a licença.
	 *  
	 * @return
	 */
	@Override
	public boolean isPrimeiraVezLicenca(){
		
		Criteria c = makeCriteria();
		c.setProjection(Projections.rowCount());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.ne("empresaId", -1));

		Long cantEmpresasBaseDados = HibernateFix.count(c.list());
		
		c = makeCriteria();
		c.setProjection(Projections.rowCount());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.ne("empresaId", -1));
		c.add(Restrictions.isNull("licenca"));
		
		Long cantEmpresasSemLicenca = HibernateFix.count(c.list());
		
		return (cantEmpresasBaseDados.equals(cantEmpresasSemLicenca));
	}
	
	public List<Empresa> listarEmpresasSemLicenca(){
		
		return null;
	}
	@SuppressWarnings("unchecked")
	public List<Empresa> obtenerIndExternoFalse() {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.eq("indExterna", Boolean.FALSE));
		c.add(Restrictions.ne("empresaId", -1));

		c.addOrder(Order.asc("nombempresa"));

		return c.list();
	}

	@SuppressWarnings("unchecked")
	public List<Empresa> obtenerIndTipo2() {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.eq("indTipo", new Short("2")));

		// Nao Buscar Empresa todas
		c.add(Restrictions.ne("empresaId", -1));

		c.addOrder(Order.asc("nombempresa"));

		return c.list();
	}

	public List<Empresa> buscarNotInPuntoVtaComissao(PuntoVenta puntoVenta) {
		StringBuilder sb = new StringBuilder();

		sb.append(" select em 				");
		sb.append(" from Empresa em 			");
		sb.append(" where em.activo = 1  	");
		sb.append(" 	and em.empresaId not in ( ");
		sb.append(" 		select pc.empresaId.empresaId from PtovtaComissao pc ");
		sb.append(" 		where pc.activo = 1 and pc.puntoventaId.puntoventaId = :puntoventaId  ");
		sb.append("			)");
		sb.append(" 	and em.empresaId != -1 ");
		sb.append(" order by em.nombempresa");

		Query query = getSession().createQuery(sb.toString());
		query.setParameter("puntoventaId", puntoVenta.getPuntoventaId());

		@SuppressWarnings("unchecked")
		List<Empresa> lsEmpresa = query.list();

		return lsEmpresa;

	}

	@SuppressWarnings("unchecked")
	@Override
	public List<InscricaoEstadual> buscaInscricoesEstaduais(Empresa empresa) {

		Criteria c = getSession().createCriteria(InscricaoEstadual.class);
		c.add(Restrictions.eq("empresa", empresa));
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));

		return c.list();
	}

	@Override
	public void actualizaInscEstadual(InscricaoEstadual inscricaoEstadual) {

		Session session = getSessionFactory().getCurrentSession();
		session.saveOrUpdate(inscricaoEstadual);
		
		if(inscricaoEstadual.getIndHabilitaIEDescentralizada()) {
			gerarSequenceBPeInscricaoEstadual(inscricaoEstadual);
		}
		
		session.flush();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Empresa> buscaLike(String nombempresa) {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.like("nombempresa", nombempresa, MatchMode.START));
		c.add(Restrictions.ne("empresaId", -1));

		c.addOrder(Order.asc("nombempresa"));

		return c.list();
	}

	@Override
	public Empresa suscribir(Empresa entity) {

		entity = super.suscribir(entity);

		getSession().flush();

		try {
			gerarSeqNumFolioSistema(entity.getEmpresaId());
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return entity;
	}
	
	@Override
	public Empresa actualizacion(Empresa entity) {
		entity = super.actualizacion(entity);
		
		try {
			gerarSeqNumFolioSistema(entity.getEmpresaId());
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return entity;
	}

	private void gerarSeqNumFolioSistema(Integer idEmpresa) throws SQLException {
		final Connection conn = dataSource.getConnection();
		try {
			geraSequence(conn, idEmpresa);
		} finally {
			conn.close();
		}
	}

	@SuppressWarnings("unchecked")
	private void geraSequence(final Connection conn, final Integer idEmpresa) throws SQLException {
		final Statement statement = conn.createStatement();
		try {
			for (FolioSistemaEnum folioSistemaEnum : FolioSistemaEnum.values()) {

				List<String> lista = getSession().createSQLQuery(obtemSqlSequence(idEmpresa, folioSistemaEnum)).list();
				for (String instrucao : lista) {
					statement.execute(instrucao);
				}
			}

		} finally {
			statement.close();
		}
	}

	private String obtemSqlSequence(Integer idEmpresa, FolioSistemaEnum folioSistemaEnum) {
		StringBuilder sql = new StringBuilder();
		sql.append(
				"SELECT CAST('CREATE SEQUENCE ' || SEQUENCIA || ' INCREMENT BY 1 START WITH 1 NOCACHE ORDER NOCYCLE' AS VARCHAR2(500)) FROM (");
		sql.append("    SELECT DISTINCT '" + folioSistemaEnum.getNome()
				+ (folioSistemaEnum.isCveEstado() ? "'||CVEESTADO||'_" : "_") + idEmpresa + "_SEQ' SEQUENCIA ");
		sql.append("    FROM ESTADO WHERE ACTIVO = 1) TMP ");
		sql.append("WHERE TMP.SEQUENCIA NOT IN( ");
		sql.append("    SELECT SEQUENCE_NAME FROM USER_SEQUENCES ");
		sql.append("    WHERE SEQUENCE_NAME LIKE '" + folioSistemaEnum.getNome() + "%_" + idEmpresa + "_SEQ')");
		return sql.toString();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Empresa> obtenerTodosIncluindoEmpresaTodas() {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.addOrder(Order.asc("nombempresa"));

		return c.list();
	}

	@Override
	@Transactional
	public ComEmpCategoria adicionarComissaoCategoria(ComEmpCategoria comEmpCategoria) {
		getSession().save(comEmpCategoria);
		return comEmpCategoria;
	}

	@Override
	@Transactional
	public void removerComissaoCategoria(ComEmpCategoria comEmpCategoria) {
		getSession().merge(comEmpCategoria);
	}

	@Override
	@Transactional
	public ComEmpFormapago adicionarComissaoFormapago(ComEmpFormapago comEmpFormapago) {
		getSession().save(comEmpFormapago);
		return comEmpFormapago;
	}

	@Override
	@Transactional
	public void removerComissaoFormapago(ComEmpFormapago comEmpFormapago) {
		getSession().merge(comEmpFormapago);
	}

	@Override
	@Transactional
	public ComEmpTipoEventoExtra adicionarComissaoTipoEventoExtra(ComEmpTipoEventoExtra comEmpTipoEventoExtra) {
		getSession().save(comEmpTipoEventoExtra);
		return comEmpTipoEventoExtra;
	}

	@Override
	@Transactional
	public void removerComissaoTipoEventoExtra(ComEmpTipoEventoExtra comEmpTipoEventoExtra) {
		getSession().merge(comEmpTipoEventoExtra);
	}

	@Override
	public ComEmpConferencia suscribir(ComEmpConferencia comEmpConferencia) {
		getSession().save(comEmpConferencia);
		return comEmpConferencia;
	}

	@Override
	public ComEmpConferencia actualizacion(ComEmpConferencia comEmpConferencia) {
		getSession().merge(comEmpConferencia);
		return comEmpConferencia;
	}
	
	@Override
	public void gerarSeqNumFolioSistema(Integer idEmpresa, String cveEstado) throws RuntimeException {
		Connection conn = null;
		try {
			if(StringUtils.isNotBlank(cveEstado)) {
				conn = dataSource.getConnection();
				if (!conn.createStatement().executeQuery("select SEQUENCE_NAME from DBA_SEQUENCES where SEQUENCE_NAME like 'FOLIO_SISTEMA_" + cveEstado + "_" + idEmpresa + "_SEQ%'").next()) {
					conn.createStatement().execute("CREATE SEQUENCE FOLIO_SISTEMA_" + cveEstado + "_" + idEmpresa + "_SEQ INCREMENT BY 1 START WITH 1 NOCACHE ORDER NOCYCLE");
				}
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}finally {
			try {
				if(conn != null && !conn.isClosed()){
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Empresa> buscarEmpresaPtoVtaComissao() {
		StringBuilder sQuery = new StringBuilder("SELECT DISTINCT em.EMPRESA_ID, em.NOMBEMPRESA, em.ACTIVO ");
		sQuery.append("FROM PTOVTA_COMISSAO ptovta ")
			  .append("JOIN EMPRESA em ON em.empresa_id = ptovta.empresa_id ")
			  .append("WHERE ptovta.activo = 1 ")
			  .append("AND em.activo = 1");
		
		Query qr = getSession().createSQLQuery(sQuery.toString())
							   .addScalar("EMPRESA_ID", IntegerType.INSTANCE)
							   .addScalar("NOMBEMPRESA", StringType.INSTANCE)
							   .addScalar("ACTIVO", BooleanType.INSTANCE)
							   .setResultTransformer(new DatosEmpresaResultTransformer());

		return qr.list();
	}
	
	private void gerarSequenceBPeInscricaoEstadual(InscricaoEstadual inscricaoEstadual) {
		StringBuilder nomeSequence = new StringBuilder(FS_BPE);
		nomeSequence.append(inscricaoEstadual.getEstado().getCveestado())
					.append("_")
					.append(inscricaoEstadual.getEmpresa().getEmpresaId())
					.append("_IE_")
					.append(inscricaoEstadual.getInscricaoestadualId())
					.append("_SEQ");
		
		if(!isSequenceBPeInscricaoEstadualCriada(nomeSequence.toString())) {
			Connection conn = null;
			try {	
				conn = dataSource.getConnection();
				conn.createStatement().execute("CREATE SEQUENCE " + nomeSequence.toString() + " MINVALUE 1 MAXVALUE 999999999 INCREMENT BY 1 START WITH 1 ORDER NOCACHE CYCLE");
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}finally {
				try {
					if(conn != null && !conn.isClosed()){
						conn.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private boolean isSequenceBPeInscricaoEstadualCriada(String nomeSequence) {
		StringBuilder sQuery = new StringBuilder();
		sQuery.append("SELECT SEQUENCE_NAME FROM USER_SEQUENCES ") 
			  .append("WHERE SEQUENCE_NAME LIKE :nomeSequence");
		
		Query qr = getSession().createSQLQuery(sQuery.toString());
		qr.setParameter("nomeSequence", nomeSequence);
		qr.setMaxResults(1);
		
		String result = (String) qr.uniqueResult();
		
		return StringUtils.isNotBlank(result);
	}

	@Override
	public String buscarNomeEmpresa(Integer empresaId) {
		Empresa empresa = obtenerID(empresaId);
		
		return empresa != null ? empresa.getNombempresa():"";
	}

}
