package com.rjconsultores.ventaboletos.auditoria.interceptor;

import java.io.Serializable;
import java.sql.Date;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.Type;

import com.rjconsultores.ventaboletos.auditoria.AuditControl;
import com.rjconsultores.ventaboletos.auditoria.AuditManager;
import com.rjconsultores.ventaboletos.dao.util.DBUtil;
import com.rjconsultores.ventaboletos.entidad.AuditLog;
import com.rjconsultores.ventaboletos.entidad.AuditService;
import com.rjconsultores.ventaboletos.entidad.Constante;
import com.rjconsultores.ventaboletos.entidad.FuncionSistema;
import com.rjconsultores.ventaboletos.entidad.Sistema;
import com.rjconsultores.ventaboletos.entidad.Usuario;
import com.rjconsultores.ventaboletos.enums.auditoria.EnumAuditAction;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;

public class AuditInterceptor extends EmptyInterceptor {
	private static final long serialVersionUID = 1L;

	private static final Logger log = LogManager.getLogger(AuditInterceptor.class);

	private static final String ACTIVO = "activo";
	
	// Considera que o ADM sempre tenha o ID igual a 1
	private Integer SISTEMA_ID = 1;

	@Override
	public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
		final SessionFactory sessionFactory = DBUtil.getInstance().getSessionFactory();
		final Session session = sessionFactory.openSession();
		
		try {
			genAudit(session, entity, EnumAuditAction.CRIACAO);
		} catch (Exception exception) {
			log.error(exception.getMessage(), exception);
		}

		if (session != null) {
			session.close();
		}

		return super.onSave(entity, id, state, propertyNames, types);
	}

	@Override
	public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
		final SessionFactory sessionFactory = DBUtil.getInstance().getSessionFactory();
		final Session session = sessionFactory.openSession();
		
		try {
			genAudit(session, entity, EnumAuditAction.ALTERACAO);
		} catch (Exception exception) {
			log.error(exception.getMessage(), exception);
		}

		if (session != null) {
			session.close();
		}
		
		return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
	}

	@Override
	public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
		final SessionFactory sessionFactory = DBUtil.getInstance().getSessionFactory();
		final Session session = sessionFactory.openSession();
		
		try {
			genAudit(session, entity, EnumAuditAction.EXCLUSAO);
		} catch (Exception exception) {
			log.error(exception.getMessage(), exception);
		}

		if (session != null) {
			session.close();
		}

		super.onDelete(entity, id, state, propertyNames, types);
	}

	private void genAudit(final Session session, Object entity, EnumAuditAction action) {
		if (!getAuditModuleService(session)) {
			return;
		}

		try {
			/**
			 * Ignorado caso a entidade for de auditoria e se não for encontrado o módulo no banco
			 */
			if (!(entity instanceof AuditLog)) {
				AuditService service = AuditManager.getINSTANCE().getCurrentService();

				if (!(entity instanceof AuditLog) && service != null && service.getModule() != null) {
					/**
					 * A auditoria não será realizada nos seguintes cenários: 1) não for encontrado o serviço no banco de dados 2) O serviço for encontrado, porém não está habilitado para auditoria
					 */
					if (service.getModule().getIndAuditable()) {
						AuditLog audit = new AuditLog();
						audit.setCreatedDate(new Date(System.currentTimeMillis()));
						audit.setEntityName(entity.getClass().getSimpleName());
						audit.setService(service);
						audit.setEntityDetail(AuditControl.toJson(entity));

						Sistema sistema = new Sistema();
						sistema.setSistemaId(SISTEMA_ID);
						audit.setSistema(sistema);

						audit.setAction(action.getId());
						audit.setUsuario(UsuarioLogado.getUsuarioLogado() != null ? UsuarioLogado.getUsuarioLogado() : new Usuario(1));

						session.persist(audit);
						session.flush();
					}
				}
			}

		} catch (Exception exception) {
			log.error(exception.getMessage(), exception);

		}
	}

	private boolean getAuditModuleService(final Session session) {
		if (AuditManager.getINSTANCE() != null && AuditManager.getINSTANCE().getAuditar() == null) {
			Criteria criteriaAuditar = session.createCriteria(Constante.class);
			criteriaAuditar.add(Restrictions.eq("nombconstante", "AUDITAR_SISTEMA"));
			criteriaAuditar.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
			Constante constante = (Constante) criteriaAuditar.uniqueResult();
			
			Boolean auditar = Boolean.FALSE;
			
			if (constante != null) {
				try {
					auditar = Boolean.valueOf(constante.getValorconstante());
				} catch (Exception e) {
					log.error(e.getMessage(), e);
				}
			}
			
			AuditManager.getINSTANCE().setAuditar(auditar);
		}
		
		if (AuditManager.getINSTANCE() == null || AuditManager.getINSTANCE().getAuditar() == null || !AuditManager.getINSTANCE().getAuditar()) {
			return false;
		}
		
		if (!AuditManager.getINSTANCE().getCheckModuleAudit()) {
			return AuditManager.getINSTANCE().getCurrentService() != null ? true : false;
		}

		AuditService auditService = null;

		Criteria criteriaFuncionSistema = session.createCriteria(FuncionSistema.class);
		criteriaFuncionSistema.add(Restrictions.eq("descruta", AuditManager.getINSTANCE().getCurrentActionService()));
		FuncionSistema funcionSistema = (FuncionSistema) criteriaFuncionSistema.uniqueResult();

		if (funcionSistema == null) {
			log.info("Função sistema " + funcionSistema + " não encontrada na base de dados.");
			AuditManager.getINSTANCE().setCurrentService(auditService);
			
			return false;
		}

		Criteria criteriaService = session.createCriteria(AuditService.class);
		criteriaService.add(Restrictions.eq("funcionSistema", funcionSistema));
		auditService = (AuditService) criteriaService.uniqueResult();

		if (auditService == null) {
			return false;
		}

		AuditManager.getINSTANCE().setCurrentService(auditService);
		AuditManager.getINSTANCE().setCheckModuleAudit(Boolean.FALSE);
		
		return true;
	}
}
